Added new fiber-based renderer for native dubbed ReactNativeFiber
Brian Vaughn committed
Dec 7, 2016 at 18:15 UTC
5266ca9312bf007b5f6868ad97d91dd687ca3517
13 files changed
+650
-82
flow/react-native-host-hooks.js
+23
-4
@@ -29,16 +29,35 @@ declare module 'TextInputState' {
29
declare module 'UIManager' {
30
declare var customBubblingEventTypes : Object;
31
declare var customDirectEventTypes : Object;
32
- declare function createView() : void;
33
- declare function manageChildren() : void;
32
+ declare function createView(
33
+ reactTag : number,
34
+ viewName : string,
35
+ rootTag : number,
36
+ props : ?Object,
37
+ ) : void;
38
+ declare function manageChildren(
39
+ containerTag : number,
40
+ moveFromIndices : Array<number>,
41
+ moveToIndices : Array<number>,
42
+ addChildReactTags : Array<number>,
43
+ addAtIndices : Array<number>,
44
+ removeAtIndices : Array<number>
45
+ ) : void;
46
declare function measure() : void;
47
declare function measureInWindow() : void;
48
declare function measureLayout() : void;
49
declare function removeRootView() : void;
50
declare function removeSubviewsFromContainerWithID() : void;
51
declare function replaceExistingNonRootView() : void;
40
- declare function setChildren() : void;
41
- declare function updateView() : void;
52
+ declare function setChildren(
53
+ containerTag : number,
54
+ reactTags : Array<number>,
55
+ ) : void;
56
+ declare function updateView(
57
+ reactTag : number,
58
+ viewName : string,
59
+ props : ?Object,
60
+ ) : void;
61
}
62
declare module 'View' {
63
declare var exports : typeof ReactComponent;
src/renderers/native/NativeMethodsMixin.js
+1
-1
@@ -150,7 +150,7 @@ var NativeMethodsMixin = {
150
);
151
152
UIManager.updateView(
153
- findNodeHandle(this),
153
+ (findNodeHandle(this) : any),
154
this.viewConfig.uiViewClassName,
155
updatePayload
156
);
src/renderers/native/ReactNative.js
+2
-64
@@ -11,67 +11,5 @@
11
*/
12
'use strict';
13
14
-// Require ReactNativeDefaultInjection first for its side effects of setting up
15
-// the JS environment
16
-var ReactNativeComponentTree = require('ReactNativeComponentTree');
17
-var ReactNativeInjection = require('ReactNativeInjection');
18
-var ReactNativeStackInjection = require('ReactNativeStackInjection');
19
-
20
-var ReactNativeMount = require('ReactNativeMount');
21
-var ReactUpdates = require('ReactUpdates');
22
-
23
-var findNodeHandle = require('findNodeHandle');
24
-
25
-ReactNativeInjection.inject();
26
-ReactNativeStackInjection.inject();
27
-
28
-var render = function(
29
- element: ReactElement<any>,
30
- mountInto: number,
31
- callback?: ?(() => void)
32
-): ?ReactComponent<any, any, any> {
33
- return ReactNativeMount.renderComponent(element, mountInto, callback);
34
-};
35
-
36
-var ReactNative = {
37
- hasReactNativeInitialized: false,
38
- findNodeHandle: findNodeHandle,
39
- render: render,
40
- unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
41
-
42
- /* eslint-disable camelcase */
43
- unstable_batchedUpdates: ReactUpdates.batchedUpdates,
44
- /* eslint-enable camelcase */
45
-
46
- unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
47
-};
48
-
49
-// Inject the runtime into a devtools global hook regardless of browser.
50
-// Allows for debugging when the hook is injected on the page.
51
-/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
52
-if (
53
- typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
54
- typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
55
- __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
56
- ComponentTree: {
57
- getClosestInstanceFromNode: function(node) {
58
- return ReactNativeComponentTree.getClosestInstanceFromNode(node);
59
- },
60
- getNodeFromInstance: function(inst) {
61
- // inst is an internal instance (but could be a composite)
62
- while (inst._renderedComponent) {
63
- inst = inst._renderedComponent;
64
- }
65
- if (inst) {
66
- return ReactNativeComponentTree.getNodeFromInstance(inst);
67
- } else {
68
- return null;
69
- }
70
- },
71
- },
72
- Mount: ReactNativeMount,
73
- Reconciler: require('ReactReconciler'),
74
- });
75
-}
76
-
77
-module.exports = ReactNative;
14
+// TODO (bvaughn) Enable Fiber experiement via ReactNativeFeatureFlags
15
+module.exports = require('ReactNativeStack');
src/renderers/native/ReactNativeComponentTree.js
+16
-4
@@ -39,6 +39,10 @@ function precacheNode(inst, tag) {
39
instanceCache[tag] = nativeInst;
40
}
41
42
+function precacheFiberNode(hostInst, tag) {
43
+ instanceCache[tag] = hostInst;
44
+}
45
+
46
function uncacheNode(inst) {
47
var tag = inst._rootNodeID;
48
if (tag) {
@@ -46,21 +50,29 @@ function uncacheNode(inst) {
50
}
51
}
52
53
+function uncacheFiberNode(tag) {
54
+ delete instanceCache[tag];
55
+}
56
+
57
function getInstanceFromTag(tag) {
58
return instanceCache[tag] || null;
59
}
60
61
function getTagFromInstance(inst) {
54
- invariant(inst._rootNodeID, 'All native instances should have a tag.');
55
- return inst._rootNodeID;
62
+ // TODO (bvaughn) Clean up once Stack is deprecated
63
+ var tag = inst._rootNodeID || inst.stateNode._nativeTag;
64
+ invariant(tag, 'All native instances should have a tag.');
65
+ return tag;
66
}
67
68
var ReactNativeComponentTree = {
69
getClosestInstanceFromNode: getInstanceFromTag,
70
getInstanceFromNode: getInstanceFromTag,
71
getNodeFromInstance: getTagFromInstance,
62
- precacheNode: precacheNode,
63
- uncacheNode: uncacheNode,
72
+ precacheFiberNode,
73
+ precacheNode,
74
+ uncacheFiberNode,
75
+ uncacheNode,
76
};
77
78
module.exports = ReactNativeComponentTree;
src/renderers/native/ReactNativeFeatureFlags.js
new
+18
@@ -0,0 +1,18 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactNativeFeatureFlags
10
+ */
11
+
12
+'use strict';
13
+
14
+var ReactNativeFeatureFlags = {
15
+ useFiber: false,
16
+};
17
+
18
+module.exports = ReactNativeFeatureFlags;
src/renderers/native/ReactNativeFiber.js
new
+395
@@ -0,0 +1,395 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactNativeFiber
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { Element } from 'React';
16
+import type { Fiber } from 'ReactFiber';
17
+import type { ReactNodeList } from 'ReactTypes';
18
+import type { ReactNativeBaseComponentViewConfig } from 'ReactNativeViewConfigRegistry';
19
+
20
+const NativeMethodsMixin = require('NativeMethodsMixin');
21
+const ReactFiberReconciler = require('ReactFiberReconciler');
22
+const ReactGenericBatching = require('ReactGenericBatching');
23
+const ReactNativeAttributePayload = require('ReactNativeAttributePayload');
24
+const ReactNativeComponentTree = require('ReactNativeComponentTree');
25
+const ReactNativeInjection = require('ReactNativeInjection');
26
+const ReactNativeTagHandles = require('ReactNativeTagHandles');
27
+const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
28
+const ReactPortal = require('ReactPortal');
29
+const UIManager = require('UIManager');
30
+
31
+const deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
32
+const findNodeHandle = require('findNodeHandle');
33
+const invariant = require('invariant');
34
+
35
+const { precacheFiberNode, uncacheFiberNode } = ReactNativeComponentTree;
36
+
37
+ReactNativeInjection.inject();
38
+
39
+type Container = number;
40
+type Instance = {
41
+ _children: Array<Instance | number>,
42
+ _nativeTag: number,
43
+ viewConfig: ReactNativeBaseComponentViewConfig,
44
+};
45
+type Props = Object;
46
+type TextInstance = number;
47
+
48
+function NativeHostComponent(tag, viewConfig) {
49
+ this._nativeTag = tag;
50
+ this._children = [];
51
+ this.viewConfig = viewConfig;
52
+}
53
+Object.assign(NativeHostComponent.prototype, NativeMethodsMixin);
54
+
55
+function recursivelyUncacheFiberNode(node : Instance | TextInstance) {
56
+ if (typeof node === 'number') { // Leaf node (eg text)
57
+ uncacheFiberNode(node);
58
+ } else {
59
+ uncacheFiberNode((node : any)._nativeTag);
60
+
61
+ (node : any)._children.forEach(recursivelyUncacheFiberNode);
62
+ }
63
+}
64
+
65
+const NativeRenderer = ReactFiberReconciler({
66
+ appendChild(
67
+ parentInstance : Instance | Container,
68
+ child : Instance | TextInstance
69
+ ) : void {
70
+ if (typeof parentInstance === 'number') {
71
+ // Root container
72
+ UIManager.setChildren(
73
+ parentInstance, // containerTag
74
+ [(child : any)._nativeTag] // reactTags
75
+ );
76
+ } else {
77
+ const children = parentInstance._children;
78
+
79
+ children.push(child);
80
+
81
+ UIManager.manageChildren(
82
+ parentInstance._nativeTag, // containerTag
83
+ [], // moveFromIndices
84
+ [], // moveToIndices
85
+ [(child : any)._nativeTag], // addChildReactTags
86
+ [children.length - 1], // addAtIndices
87
+ [], // removeAtIndices
88
+ );
89
+ }
90
+ },
91
+
92
+ appendInitialChild(parentInstance : Instance, child : Instance | TextInstance) : void {
93
+ if (typeof child === 'number') {
94
+ parentInstance._children.push(child);
95
+ } else {
96
+ parentInstance._children.push(child);
97
+ }
98
+ },
99
+
100
+ commitTextUpdate(
101
+ textInstance : TextInstance,
102
+ oldText : string,
103
+ newText : string
104
+ ) : void {
105
+ UIManager.updateView(
106
+ textInstance, // reactTag
107
+ 'RCTRawText', // viewName
108
+ {text: newText}, // props
109
+ );
110
+ },
111
+
112
+ commitUpdate(
113
+ instance : Instance,
114
+ type : string,
115
+ oldProps : Props,
116
+ newProps : Props,
117
+ rootContainerInstance : Object,
118
+ internalInstanceHandle : Object
119
+ ) : void {
120
+ const viewConfig = instance.viewConfig;
121
+
122
+ precacheFiberNode(internalInstanceHandle, instance._nativeTag);
123
+
124
+ const updatePayload = ReactNativeAttributePayload.diff(
125
+ oldProps,
126
+ newProps,
127
+ viewConfig.validAttributes
128
+ );
129
+
130
+ UIManager.updateView(
131
+ (instance : any)._nativeTag, // reactTag
132
+ viewConfig.uiViewClassName, // viewName
133
+ updatePayload, // props
134
+ );
135
+ },
136
+
137
+ createInstance(
138
+ type : string,
139
+ props : Props,
140
+ rootContainerInstance : Container,
141
+ hostContext : string | null,
142
+ internalInstanceHandle : Object
143
+ ) : Instance {
144
+ const tag = ReactNativeTagHandles.allocateTag();
145
+ const viewConfig = ReactNativeViewConfigRegistry.get(type);
146
+
147
+ if (__DEV__) {
148
+ for (let key in viewConfig.validAttributes) {
149
+ if (props.hasOwnProperty(key)) {
150
+ deepFreezeAndThrowOnMutationInDev(props[key]);
151
+ }
152
+ }
153
+ }
154
+
155
+ const updatePayload = ReactNativeAttributePayload.create(
156
+ props,
157
+ viewConfig.validAttributes
158
+ );
159
+
160
+ UIManager.createView(
161
+ tag, // reactTag
162
+ viewConfig.uiViewClassName, // viewName
163
+ rootContainerInstance, // rootTag
164
+ updatePayload, // props
165
+ );
166
+
167
+ const component = new NativeHostComponent(tag, viewConfig);
168
+
169
+ precacheFiberNode(internalInstanceHandle, tag);
170
+
171
+ return component;
172
+ },
173
+
174
+ createTextInstance(
175
+ text : string,
176
+ rootContainerInstance : Container,
177
+ internalInstanceHandle : Object,
178
+ ) : TextInstance {
179
+ const tag = ReactNativeTagHandles.allocateTag();
180
+
181
+ UIManager.createView(
182
+ tag, // reactTag
183
+ 'RCTRawText', // viewName
184
+ rootContainerInstance, // rootTag
185
+ {text: text} // props
186
+ );
187
+
188
+ precacheFiberNode(internalInstanceHandle, tag);
189
+
190
+ return tag;
191
+ },
192
+
193
+ finalizeInitialChildren(
194
+ parentInstance : Instance,
195
+ type : string,
196
+ props : Props,
197
+ rootContainerInstance : Container,
198
+ ) : void {
199
+ // Map from child objects to native tags.
200
+ // Either way we need to pass a copy of the Array to prevent it from being frozen.
201
+ const nativeTags = parentInstance._children.map(
202
+ (child) => typeof child === 'number'
203
+ ? child // Leaf node (eg text)
204
+ : child._nativeTag
205
+ );
206
+
207
+ UIManager.setChildren(
208
+ parentInstance._nativeTag, // containerTag
209
+ nativeTags // reactTags
210
+ );
211
+ },
212
+
213
+ getChildHostContext(
214
+ parentHostContext : string | null,
215
+ type : string
216
+ ) {
217
+ return parentHostContext;
218
+ },
219
+
220
+ insertBefore(
221
+ parentInstance : Instance | Container,
222
+ child : Instance | TextInstance,
223
+ beforeChild : Instance | TextInstance
224
+ ) : void {
225
+ // TODO (bvaughn): Remove this check when...
226
+ // We create a wrapper object for the container in ReactNative render()
227
+ // Or we refactor to remove wrapper objects entirely.
228
+ // For more info on pros/cons see PR #8560 description.
229
+ invariant(
230
+ typeof parentInstance !== 'number',
231
+ 'Container does not support insertBefore operation',
232
+ );
233
+
234
+ const children = (parentInstance : any)._children;
235
+
236
+ const beforeChildIndex = children.indexOf(beforeChild);
237
+ const index = children.indexOf(child);
238
+
239
+ // Move existing child or add new child?
240
+ if (index >= 0) {
241
+ children.splice(index, 1);
242
+ children.splice(beforeChildIndex, 0, child);
243
+
244
+ UIManager.manageChildren(
245
+ (parentInstance : any)._nativeTag, // containerID
246
+ [index], // moveFromIndices
247
+ [beforeChildIndex], // moveToIndices
248
+ [], // addChildReactTags
249
+ [], // addAtIndices
250
+ [], // removeAtIndices
251
+ );
252
+ } else {
253
+ children.splice(beforeChildIndex, 0, child);
254
+
255
+ UIManager.manageChildren(
256
+ (parentInstance : any)._nativeTag, // containerID
257
+ [], // moveFromIndices
258
+ [], // moveToIndices
259
+ [(child : any)._nativeTag], // addChildReactTags
260
+ [beforeChildIndex], // addAtIndices
261
+ [], // removeAtIndices
262
+ );
263
+ }
264
+ },
265
+
266
+ prepareForCommit() : void {
267
+ // Noop
268
+ },
269
+
270
+ prepareUpdate(
271
+ instance : Instance,
272
+ type : string,
273
+ oldProps : Props,
274
+ newProps : Props
275
+ ) : boolean {
276
+ return true;
277
+ },
278
+
279
+ removeChild(
280
+ parentInstance : Instance | Container,
281
+ child : Instance | TextInstance
282
+ ) : void {
283
+ recursivelyUncacheFiberNode(child);
284
+
285
+ if (typeof parentInstance === 'number') {
286
+ UIManager.manageChildren(
287
+ parentInstance, // containerID
288
+ [], // moveFromIndices
289
+ [], // moveToIndices
290
+ [], // addChildReactTags
291
+ [], // addAtIndices
292
+ [0], // removeAtIndices
293
+ );
294
+ } else {
295
+ const children = parentInstance._children;
296
+ const index = children.indexOf(child);
297
+
298
+ children.splice(index, 1);
299
+
300
+ UIManager.manageChildren(
301
+ parentInstance._nativeTag, // containerID
302
+ [], // moveFromIndices
303
+ [], // moveToIndices
304
+ [], // addChildReactTags
305
+ [], // addAtIndices
306
+ [index], // removeAtIndices
307
+ );
308
+ }
309
+ },
310
+
311
+ resetAfterCommit() : void {
312
+ // Noop
313
+ },
314
+
315
+ resetTextContent(instance : Instance) : void {
316
+ // Noop
317
+ },
318
+
319
+ scheduleAnimationCallback: global.requestAnimationFrame,
320
+
321
+ scheduleDeferredCallback: global.requestIdleCallback,
322
+
323
+ shouldSetTextContent(props : Props) : boolean {
324
+ // TODO (bvaughn) Revisit this decision.
325
+ // Always returning false simplifies the createInstance() implementation,
326
+ // But creates an additional child Fiber for raw text children.
327
+ // No additional native views are created though.
328
+ // It's not clear to me which is better so I'm deferring for now.
329
+ // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
330
+ return false;
331
+ },
332
+
333
+ useSyncScheduling: true,
334
+});
335
+
336
+ReactGenericBatching.injection.injectFiberBatchedUpdates(
337
+ NativeRenderer.batchedUpdates
338
+);
339
+
340
+const roots = new Map();
341
+
342
+findNodeHandle.injection.injectFindNode(
343
+ (fiber: Fiber) => {
344
+ const instance: any = NativeRenderer.findHostInstance(fiber);
345
+ return instance ? instance._nativeTag : null;
346
+ }
347
+);
348
+findNodeHandle.injection.injectFindRootNodeID(
349
+ (instance) => instance._nativeTag
350
+);
351
+
352
+const ReactNative = {
353
+ findNodeHandle,
354
+
355
+ render(element : Element<any>, containerTag : any, callback: ?Function) {
356
+ let root = roots.get(containerTag);
357
+
358
+ if (!root) {
359
+ // TODO (bvaughn): If we decide to keep the wrapper component,
360
+ // We could create a wrapper for containerTag as well to reduce special casing.
361
+ root = NativeRenderer.mountContainer(element, containerTag, null, callback);
362
+
363
+ roots.set(containerTag, root);
364
+ } else {
365
+ NativeRenderer.updateContainer(element, root, null, callback);
366
+ }
367
+
368
+ return NativeRenderer.getPublicRootInstance(root);
369
+ },
370
+
371
+ unmountComponentAtNode(containerTag : number) {
372
+ const root = roots.get(containerTag);
373
+ if (root) {
374
+ // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
375
+ roots.delete(containerTag);
376
+ NativeRenderer.unmountContainer(root);
377
+ }
378
+ },
379
+
380
+ unmountComponentAtNodeAndRemoveContainer(containerTag: number) {
381
+ ReactNative.unmountComponentAtNode(containerTag);
382
+
383
+ // Call back into native to remove all of the subviews from this container
384
+ UIManager.removeRootView(containerTag);
385
+ },
386
+
387
+ unstable_createPortal(children: ReactNodeList, containerTag : number, key : ?string = null) {
388
+ return ReactPortal.createPortal(children, containerTag, null, key);
389
+ },
390
+
391
+ unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
392
+
393
+};
394
+
395
+module.exports = ReactNative;
src/renderers/native/ReactNativeGlobalResponderHandler.js
+2
-1
@@ -15,8 +15,9 @@ var UIManager = require('UIManager');
15
var ReactNativeGlobalResponderHandler = {
16
onChange: function(from, to, blockNativeResponder) {
17
if (to !== null) {
18
+ // TODO (bvaughn) Clean up once Stack is deprecated
19
UIManager.setJSResponder(
19
- to._rootNodeID,
20
+ to._rootNodeID || to.stateNode._nativeTag,
21
blockNativeResponder
22
);
23
} else {
src/renderers/native/ReactNativeStack.js
new
+81
@@ -0,0 +1,81 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactNativeStack
10
+ * @flow
11
+ */
12
+'use strict';
13
+
14
+var ReactNativeComponentTree = require('ReactNativeComponentTree');
15
+var ReactNativeInjection = require('ReactNativeInjection');
16
+var ReactNativeStackInjection = require('ReactNativeStackInjection');
17
+var ReactNativeMount = require('ReactNativeMount');
18
+var ReactUpdates = require('ReactUpdates');
19
+
20
+var findNodeHandle = require('findNodeHandle');
21
+
22
+ReactNativeInjection.inject();
23
+ReactNativeStackInjection.inject();
24
+
25
+var render = function(
26
+ element: ReactElement<any>,
27
+ mountInto: number,
28
+ callback?: ?(() => void)
29
+): ?ReactComponent<any, any, any> {
30
+ return ReactNativeMount.renderComponent(element, mountInto, callback);
31
+};
32
+
33
+findNodeHandle.injection.injectFindNode(
34
+ (instance) => instance.getHostNode()
35
+);
36
+findNodeHandle.injection.injectFindRootNodeID(
37
+ (instance) => instance._rootNodeID
38
+);
39
+
40
+var ReactNative = {
41
+ hasReactNativeInitialized: false,
42
+ findNodeHandle: findNodeHandle,
43
+ render: render,
44
+ unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
45
+
46
+ /* eslint-disable camelcase */
47
+ unstable_batchedUpdates: ReactUpdates.batchedUpdates,
48
+ /* eslint-enable camelcase */
49
+
50
+ unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
51
+};
52
+
53
+// Inject the runtime into a devtools global hook regardless of browser.
54
+// Allows for debugging when the hook is injected on the page.
55
+/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
56
+if (
57
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
58
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
59
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
60
+ ComponentTree: {
61
+ getClosestInstanceFromNode: function(node) {
62
+ return ReactNativeComponentTree.getClosestInstanceFromNode(node);
63
+ },
64
+ getNodeFromInstance: function(inst) {
65
+ // inst is an internal instance (but could be a composite)
66
+ while (inst._renderedComponent) {
67
+ inst = inst._renderedComponent;
68
+ }
69
+ if (inst) {
70
+ return ReactNativeComponentTree.getNodeFromInstance(inst);
71
+ } else {
72
+ return null;
73
+ }
74
+ },
75
+ },
76
+ Mount: ReactNativeMount,
77
+ Reconciler: require('ReactReconciler'),
78
+ });
79
+}
80
+
81
+module.exports = ReactNative;
src/renderers/native/ReactNativeStackInjection.js
+8
@@ -30,6 +30,7 @@ var ReactNativeTextComponent = require('ReactNativeTextComponent');
30
var ReactSimpleEmptyComponent = require('ReactSimpleEmptyComponent');
31
var ReactUpdates = require('ReactUpdates');
32
33
+var findNodeHandle = require('findNodeHandle');
34
var invariant = require('invariant');
35
36
function inject() {
@@ -61,6 +62,13 @@ function inject() {
62
);
63
};
64
65
+ findNodeHandle.injection.injectFindNode(
66
+ (instance) => instance.getHostNode()
67
+ );
68
+ findNodeHandle.injection.injectFindRootNodeID(
69
+ (instance) => instance._rootNodeID
70
+ );
71
+
72
ReactEmptyComponent.injection.injectEmptyComponentFactory(EmptyComponent);
73
74
ReactHostComponent.injection.injectTextComponentClass(
src/renderers/native/ReactNativeViewConfigRegistry.js
new
+50
@@ -0,0 +1,50 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactNativeViewConfigRegistry
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+const invariant = require('invariant');
16
+
17
+export type ReactNativeBaseComponentViewConfig = {
18
+ validAttributes: Object,
19
+ uiViewClassName: string,
20
+ propTypes?: Object,
21
+};
22
+
23
+const viewConfigs = new Map();
24
+
25
+const prefix = 'topsecret-';
26
+
27
+const ReactNativeViewConfigRegistry = {
28
+ register(viewConfig : ReactNativeBaseComponentViewConfig) {
29
+ const name = viewConfig.uiViewClassName;
30
+ invariant(
31
+ !viewConfigs.has(name),
32
+ 'Tried to register two views with the same name %s',
33
+ name
34
+ );
35
+ const secretName = prefix + name;
36
+ viewConfigs.set(secretName, viewConfig);
37
+ return secretName;
38
+ },
39
+ get(secretName: string) {
40
+ const config = viewConfigs.get(secretName);
41
+ invariant(
42
+ config,
43
+ 'View config not found for name %s',
44
+ secretName
45
+ );
46
+ return config;
47
+ },
48
+};
49
+
50
+module.exports = ReactNativeViewConfigRegistry;
src/renderers/native/__mocks__/ReactNative.js
new
+16
@@ -0,0 +1,16 @@
1
+/**
2
+ * Copyright 2013-2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ */
9
+
10
+'use strict';
11
+
12
+const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
13
+
14
+module.exports = ReactNativeFeatureFlags.useFiber
15
+ ? require('ReactNativeFiber')
16
+ : require('ReactNativeStack');
src/renderers/native/createReactNativeComponentClass.js
+19
-5
@@ -12,23 +12,35 @@
12
13
'use strict';
14
15
-var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
15
+const ReactNativeBaseComponent = require('ReactNativeBaseComponent');
16
+const ReactNativeViewConfigRegistry = require('ReactNativeViewConfigRegistry');
17
+const ReactNativeFeatureFlags = require('ReactNativeFeatureFlags');
18
19
// See also ReactNativeBaseComponent
20
type ReactNativeBaseComponentViewConfig = {
21
validAttributes: Object,
22
uiViewClassName: string,
23
propTypes?: Object,
22
-}
24
+};
25
+
26
+/**
27
+ * @param {string} config iOS View configuration.
28
+ * @private
29
+ */
30
+const createReactNativeFiberComponentClass = function(
31
+ viewConfig: ReactNativeBaseComponentViewConfig
32
+): string {
33
+ return ReactNativeViewConfigRegistry.register(viewConfig);
34
+};
35
36
/**
37
* @param {string} config iOS View configuration.
38
* @private
39
*/
28
-var createReactNativeComponentClass = function(
40
+const createReactNativeComponentClass = function(
41
viewConfig: ReactNativeBaseComponentViewConfig
42
): ReactClass<any> {
31
- var Constructor = function(element) {
43
+ const Constructor = function(element) {
44
this._currentElement = element;
45
this._topLevelWrapper = null;
46
this._hostParent = null;
@@ -45,4 +57,6 @@ var createReactNativeComponentClass = function(
57
return ((Constructor: any): ReactClass<any>);
58
};
59
48
-module.exports = createReactNativeComponentClass;
60
+module.exports = ReactNativeFeatureFlags.useFiber
61
+ ? createReactNativeFiberComponentClass
62
+ : createReactNativeComponentClass;
src/renderers/native/findNodeHandle.js
+19
-3
@@ -50,6 +50,9 @@ import type { ReactInstance } from 'ReactInstanceType';
50
* nodeHandle N/A rootNodeID tag
51
*/
52
53
+let injectedFindNode;
54
+let injectedFindRootNodeID;
55
+
56
function findNodeHandle(componentOrHandle: any): ?number {
57
if (__DEV__) {
58
// TODO: fix this unsafe cast to work with Fiber.
@@ -82,9 +85,9 @@ function findNodeHandle(componentOrHandle: any): ?number {
85
// ReactInstanceMap.get here will always succeed for mounted components
86
var internalInstance = ReactInstanceMap.get(component);
87
if (internalInstance) {
85
- return internalInstance.getHostNode();
88
+ return injectedFindNode(internalInstance);
89
} else {
87
- var rootNodeID = component._rootNodeID;
90
+ var rootNodeID = injectedFindRootNodeID(component);
91
if (rootNodeID) {
92
return rootNodeID;
93
} else {
@@ -92,7 +95,10 @@ function findNodeHandle(componentOrHandle: any): ?number {
95
(
96
// Native
97
typeof component === 'object' &&
95
- '_rootNodeID' in component
98
+ (
99
+ '_rootNodeID' in component || // TODO (bvaughn) Clean up once Stack is deprecated
100
+ '_nativeTag' in component
101
+ )
102
) || (
103
// Composite
104
component.render != null &&
@@ -112,4 +118,14 @@ function findNodeHandle(componentOrHandle: any): ?number {
118
}
119
}
120
121
+// Fiber and stack implementations differ; each must inject a strategy
122
+findNodeHandle.injection = {
123
+ injectFindNode(findNode) {
124
+ injectedFindNode = findNode;
125
+ },
126
+ injectFindRootNodeID(findRootNodeID) {
127
+ injectedFindRootNodeID = findRootNodeID;
128
+ },
129
+};
130
+
131
module.exports = findNodeHandle;