@samitouri / QOS-React / commits / 5266ca9312

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;