Fork React Native render into an "RT" renderer (#11072)
This is an experimental new protocol for some experiments we want to play with. To make that easier, I'm just going to fork it. This experiment won't use the event system so I by-pass it and just invoke functions on the props object for now. I also fork the UIManager into a new RTManager.
Sebastian Markbåge committed
Oct 3, 2017 at 18:48 UTC
761decb352a775ca39aa0fec31f01534ab52c5c0
12 files changed
+739
-27
flow/react-native-host-hooks.js
+32
@@ -86,3 +86,35 @@ declare module 'UIManager' {
86
declare module 'View' {
87
declare var exports : typeof React$Component;
88
}
89
+
90
+declare module 'RTManager' {
91
+ declare function createNode(
92
+ tag : number,
93
+ classType : string,
94
+ props : ?Object,
95
+ ) : void;
96
+ declare function appendChildToDetachedParent(
97
+ parentTag : number,
98
+ childTag : number,
99
+ ) : void;
100
+
101
+ declare function beginUpdates() : void;
102
+
103
+ declare function appendChild(
104
+ parentTag : number,
105
+ childTag : number,
106
+ ) : void;
107
+ declare function prependChild(
108
+ childTag : number,
109
+ beforeTag : number,
110
+ ) : void;
111
+ declare function deleteChild(
112
+ childTag : number,
113
+ ) : void;
114
+ declare function updateNode(
115
+ tag : number,
116
+ props : ?Object,
117
+ ) : void;
118
+
119
+ declare function completeUpdates() : void;
120
+}
scripts/rollup/bundles.js
+34
@@ -288,6 +288,40 @@ const bundles = [
288
useFiber: true,
289
},
290
291
+ /******* React Native *******/
292
+ {
293
+ babelOpts: babelOptsReact,
294
+ bundleTypes: [RN_DEV, RN_PROD],
295
+ config: {
296
+ destDir: 'build/',
297
+ moduleName: 'ReactNativeRTFiber',
298
+ sourceMap: false,
299
+ },
300
+ entry: 'src/renderers/native-rt/ReactNativeRTFiberEntry',
301
+ externals: [
302
+ 'ExceptionsManager',
303
+ 'InitializeCore',
304
+ 'Platform',
305
+ 'RCTEventEmitter',
306
+ 'RTManager',
307
+ 'prop-types/checkPropTypes',
308
+ ],
309
+ hasteName: 'ReactNativeRTFiber',
310
+ isRenderer: true,
311
+ label: 'native-rt-fiber',
312
+ manglePropertiesOnProd: false,
313
+ name: 'react-native-rt-renderer',
314
+ paths: [
315
+ 'src/renderers/native/**/*.js', // This is used since we reuse the error dialog code
316
+ 'src/renderers/native-rt/**/*.js',
317
+ 'src/renderers/shared/**/*.js',
318
+
319
+ 'src/ReactVersion.js',
320
+ 'src/shared/**/*.js',
321
+ ],
322
+ useFiber: true,
323
+ },
324
+
325
/******* React Test Renderer *******/
326
{
327
babelOpts: babelOptsReact,
scripts/rollup/results.json
+35
-27
@@ -25,28 +25,28 @@
25
"gzip": 6703
26
},
27
"react-dom.development.js (UMD_DEV)": {
28
- "size": 613875,
29
- "gzip": 141842
28
+ "size": 614249,
29
+ "gzip": 141958
30
},
31
"react-dom.production.min.js (UMD_PROD)": {
32
- "size": 99469,
33
- "gzip": 31443
32
+ "size": 99500,
33
+ "gzip": 31459
34
},
35
"react-dom.development.js (NODE_DEV)": {
36
- "size": 576136,
37
- "gzip": 133165
36
+ "size": 576510,
37
+ "gzip": 133279
38
},
39
"react-dom.production.min.js (NODE_PROD)": {
40
- "size": 104422,
41
- "gzip": 32815
40
+ "size": 104453,
41
+ "gzip": 32832
42
},
43
"ReactDOMFiber-dev.js (FB_DEV)": {
44
- "size": 573085,
45
- "gzip": 132629
44
+ "size": 573391,
45
+ "gzip": 132712
46
},
47
"ReactDOMFiber-prod.js (FB_PROD)": {
48
- "size": 409433,
49
- "gzip": 91568
48
+ "size": 409575,
49
+ "gzip": 91600
50
},
51
"react-dom-test-utils.development.js (NODE_DEV)": {
52
"size": 41660,
@@ -81,36 +81,36 @@
81
"gzip": 15465
82
},
83
"react-dom-server.browser.development.js (UMD_DEV)": {
84
- "size": 129517,
85
- "gzip": 33434
84
+ "size": 129604,
85
+ "gzip": 33468
86
},
87
"react-dom-server.browser.production.min.js (UMD_PROD)": {
88
- "size": 14948,
89
- "gzip": 5844
88
+ "size": 14959,
89
+ "gzip": 5848
90
},
91
"react-dom-server.browser.development.js (NODE_DEV)": {
92
- "size": 99439,
93
- "gzip": 26329
92
+ "size": 99526,
93
+ "gzip": 26362
94
},
95
"react-dom-server.browser.production.min.js (NODE_PROD)": {
96
- "size": 14878,
96
+ "size": 14889,
97
"gzip": 5844
98
},
99
"ReactDOMServer-dev.js (FB_DEV)": {
100
- "size": 98860,
101
- "gzip": 26238
100
+ "size": 98947,
101
+ "gzip": 26272
102
},
103
"ReactDOMServer-prod.js (FB_PROD)": {
104
- "size": 42180,
105
- "gzip": 11786
104
+ "size": 42275,
105
+ "gzip": 11813
106
},
107
"react-dom-server.node.development.js (NODE_DEV)": {
108
- "size": 101717,
109
- "gzip": 26882
108
+ "size": 101804,
109
+ "gzip": 26916
110
},
111
"react-dom-server.node.production.min.js (NODE_PROD)": {
112
- "size": 15803,
113
- "gzip": 6168
112
+ "size": 15814,
113
+ "gzip": 6172
114
},
115
"react-art.development.js (UMD_DEV)": {
116
"size": 366731,
@@ -187,6 +187,14 @@
187
"react-dom-node-stream.production.min.js (NODE_PROD)": {
188
"size": 19585,
189
"gzip": 7520
190
+ },
191
+ "ReactNativeRTFiber-dev.js (RN_DEV)": {
192
+ "size": 218534,
193
+ "gzip": 37944
194
+ },
195
+ "ReactNativeRTFiber-prod.js (RN_PROD)": {
196
+ "size": 167912,
197
+ "gzip": 28774
198
}
199
}
200
}
\ No newline at end of file
src/__mocks__/RTManager.js
new
+26
@@ -0,0 +1,26 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+'use strict';
9
+
10
+// Mock of the Native Hooks
11
+
12
+var RCTRTManager = {
13
+ createNode: jest.fn(function createView(tag, classType, props) {}),
14
+ appendChildToDetachedParent: jest.fn(function appendChildToDetachedParent(
15
+ parentTag,
16
+ childTag,
17
+ ) {}),
18
+ beginUpdates: jest.fn(function beginUpdates() {}),
19
+ appendChild: jest.fn(function appendChild(parentTag, childTag) {}),
20
+ prependChild: jest.fn(function prependChild(childTag, beforeTag) {}),
21
+ deleteChild: jest.fn(function deleteChild(childTag) {}),
22
+ updateNode: jest.fn(function updateNode(tag, props) {}),
23
+ completeUpdates: jest.fn(function completeUpdates() {}),
24
+};
25
+
26
+module.exports = RCTRTManager;
src/renderers/native-rt/ReactNativeRTComponentTree.js
new
+47
@@ -0,0 +1,47 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTComponentTree
8
+ * @flow
9
+ */
10
+
11
+'use strict';
12
+
13
+import type {Fiber} from 'ReactFiber';
14
+
15
+var instanceCache: {[key: number]: Fiber} = {};
16
+var instanceProps: {[key: number]: Object} = {};
17
+
18
+function precacheFiberNode(fiber: Fiber, tag: number): void {
19
+ instanceCache[tag] = fiber;
20
+}
21
+
22
+function getFiberFromTag(tag: number): null | Fiber {
23
+ return instanceCache[tag] || null;
24
+}
25
+
26
+function uncacheFiberNode(tag: number): void {
27
+ delete instanceCache[tag];
28
+ delete instanceProps[tag];
29
+}
30
+
31
+function getFiberCurrentPropsFromTag(tag: number): null | Object {
32
+ return instanceProps[tag] || null;
33
+}
34
+
35
+function updateFiberProps(tag: number, props: Object): void {
36
+ instanceProps[tag] = props;
37
+}
38
+
39
+var ReactNativeRTComponentTree = {
40
+ precacheFiberNode,
41
+ uncacheFiberNode,
42
+ getFiberFromTag,
43
+ getFiberCurrentPropsFromTag,
44
+ updateFiberProps,
45
+};
46
+
47
+module.exports = ReactNativeRTComponentTree;
src/renderers/native-rt/ReactNativeRTEventEmitter.js
new
+45
@@ -0,0 +1,45 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTEventEmitter
8
+ * @flow
9
+ */
10
+'use strict';
11
+
12
+var ReactNativeRTComponentTree = require('ReactNativeRTComponentTree');
13
+var ReactGenericBatching = require('ReactGenericBatching');
14
+
15
+var ReactNativeRTEventEmitter = {
16
+ /**
17
+ * Publicly exposed method on module for native objc to invoke when a top
18
+ * level event is extracted.
19
+ * @param {rootNodeID} rootNodeID React root node ID that event occurred on.
20
+ * @param {TopLevelType} topLevelType Top level type of event.
21
+ * @param {object} nativeEventParam Object passed from native.
22
+ */
23
+ receiveEvent: function(
24
+ rootNodeID: number,
25
+ topLevelType: string,
26
+ nativeEventParam: Object,
27
+ ) {
28
+ var nativeEvent = nativeEventParam;
29
+ var props = ReactNativeRTComponentTree.getFiberCurrentPropsFromTag(
30
+ rootNodeID,
31
+ );
32
+ if (props == null) {
33
+ return;
34
+ }
35
+ var eventHandler = props[topLevelType];
36
+ if (typeof eventHandler !== 'function') {
37
+ return;
38
+ }
39
+ ReactGenericBatching.batchedUpdates(function() {
40
+ eventHandler(nativeEvent);
41
+ });
42
+ },
43
+};
44
+
45
+module.exports = ReactNativeRTEventEmitter;
src/renderers/native-rt/ReactNativeRTFiberEntry.js
new
+103
@@ -0,0 +1,103 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTFiberEntry
8
+ * @flow
9
+ */
10
+
11
+'use strict';
12
+
13
+const ReactFiberErrorLogger = require('ReactFiberErrorLogger');
14
+const ReactGenericBatching = require('ReactGenericBatching');
15
+const ReactNativeFiberErrorDialog = require('ReactNativeFiberErrorDialog'); // Reused from RN, seems fine?
16
+const ReactPortal = require('ReactPortal');
17
+const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree');
18
+const ReactNativeRTFiberRenderer = require('ReactNativeRTFiberRenderer');
19
+const ReactNativeRTFiberInspector = require('ReactNativeRTFiberInspector');
20
+const ReactVersion = require('ReactVersion');
21
+
22
+const {injectInternals} = require('ReactFiberDevToolsHook');
23
+
24
+import type {ReactNativeRTType} from 'ReactNativeRTTypes';
25
+import type {ReactNodeList} from 'ReactTypes';
26
+
27
+/**
28
+ * Make sure essential globals are available and are patched correctly. Please don't remove this
29
+ * line. Bundles created by react-packager `require` it before executing any application code. This
30
+ * ensures it exists in the dependency graph and can be `require`d.
31
+ * TODO: require this in packager, not in React #10932517
32
+ */
33
+require('InitializeCore');
34
+
35
+var RCTEventEmitter = require('RCTEventEmitter');
36
+var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
37
+
38
+/**
39
+ * Register the event emitter with the native bridge
40
+ */
41
+RCTEventEmitter.register(ReactNativeEventEmitter);
42
+
43
+ReactGenericBatching.injection.injectFiberBatchedUpdates(
44
+ ReactNativeRTFiberRenderer.batchedUpdates,
45
+);
46
+
47
+const roots = new Map();
48
+
49
+// Intercept lifecycle errors and ensure they are shown with the correct stack
50
+// trace within the native redbox component.
51
+ReactFiberErrorLogger.injection.injectDialog(
52
+ ReactNativeFiberErrorDialog.showDialog,
53
+);
54
+
55
+const ReactNativeRTFiber: ReactNativeRTType = {
56
+ render(element: React$Element<any>, containerTag: any, callback: ?Function) {
57
+ let root = roots.get(containerTag);
58
+
59
+ if (!root) {
60
+ // TODO (bvaughn): If we decide to keep the wrapper component,
61
+ // We could create a wrapper for containerTag as well to reduce special casing.
62
+ root = ReactNativeRTFiberRenderer.createContainer(containerTag);
63
+ roots.set(containerTag, root);
64
+ }
65
+ ReactNativeRTFiberRenderer.updateContainer(element, root, null, callback);
66
+
67
+ return ReactNativeRTFiberRenderer.getPublicRootInstance(root);
68
+ },
69
+
70
+ unmountComponentAtNode(containerTag: number) {
71
+ const root = roots.get(containerTag);
72
+ if (root) {
73
+ // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
74
+ ReactNativeRTFiberRenderer.updateContainer(null, root, null, () => {
75
+ roots.delete(containerTag);
76
+ });
77
+ }
78
+ },
79
+
80
+ createPortal(
81
+ children: ReactNodeList,
82
+ containerTag: number,
83
+ key: ?string = null,
84
+ ) {
85
+ return ReactPortal.createPortal(children, containerTag, null, key);
86
+ },
87
+
88
+ unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
89
+
90
+ flushSync: ReactNativeRTFiberRenderer.flushSync,
91
+};
92
+
93
+injectInternals({
94
+ findFiberByHostInstance: ReactNativeRTComponentTree.getFiberFromTag,
95
+ findHostInstanceByFiber: ReactNativeRTFiberRenderer.findHostInstance,
96
+ getInspectorDataForViewTag: ReactNativeRTFiberInspector.getInspectorDataForViewTag,
97
+ // This is an enum because we may add more (e.g. profiler build)
98
+ bundleType: __DEV__ ? 1 : 0,
99
+ version: ReactVersion,
100
+ rendererPackageName: 'react-native-rt',
101
+});
102
+
103
+module.exports = ReactNativeRTFiber;
src/renderers/native-rt/ReactNativeRTFiberInspector.js
new
+108
@@ -0,0 +1,108 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTFiberInspector
8
+ * @flow
9
+ */
10
+'use strict';
11
+
12
+const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree');
13
+const ReactFiberTreeReflection = require('ReactFiberTreeReflection');
14
+const getComponentName = require('getComponentName');
15
+const emptyObject = require('fbjs/lib/emptyObject');
16
+const ReactTypeOfWork = require('ReactTypeOfWork');
17
+const invariant = require('fbjs/lib/invariant');
18
+
19
+const {getFiberFromTag} = ReactNativeRTComponentTree;
20
+const {findCurrentFiberUsingSlowPath} = ReactFiberTreeReflection;
21
+const {HostComponent} = ReactTypeOfWork;
22
+
23
+let getInspectorDataForViewTag;
24
+
25
+if (__DEV__) {
26
+ var traverseOwnerTreeUp = function(hierarchy, instance: any) {
27
+ if (instance) {
28
+ hierarchy.unshift(instance);
29
+ traverseOwnerTreeUp(hierarchy, instance._debugOwner);
30
+ }
31
+ };
32
+
33
+ var getOwnerHierarchy = function(instance: any) {
34
+ var hierarchy = [];
35
+ traverseOwnerTreeUp(hierarchy, instance);
36
+ return hierarchy;
37
+ };
38
+
39
+ var lastNonHostInstance = function(hierarchy) {
40
+ for (let i = hierarchy.length - 1; i > 1; i--) {
41
+ const instance = hierarchy[i];
42
+
43
+ if (instance.tag !== HostComponent) {
44
+ return instance;
45
+ }
46
+ }
47
+ return hierarchy[0];
48
+ };
49
+
50
+ var getHostProps = function(fiber) {
51
+ const host = ReactFiberTreeReflection.findCurrentHostFiber(fiber);
52
+ if (host) {
53
+ return host.memoizedProps || emptyObject;
54
+ }
55
+ return emptyObject;
56
+ };
57
+
58
+ var createHierarchy = function(fiberHierarchy) {
59
+ return fiberHierarchy.map(fiber => ({
60
+ name: getComponentName(fiber),
61
+ getInspectorData: findNodeHandle => ({
62
+ measure: callback => invariant(false, 'Measure not implemented yet'),
63
+ props: getHostProps(fiber),
64
+ source: fiber._debugSource,
65
+ }),
66
+ }));
67
+ };
68
+
69
+ getInspectorDataForViewTag = function(viewTag: number): Object {
70
+ const closestInstance = getFiberFromTag(viewTag);
71
+
72
+ // Handle case where user clicks outside of ReactNative
73
+ if (!closestInstance) {
74
+ return {
75
+ hierarchy: [],
76
+ props: emptyObject,
77
+ selection: null,
78
+ source: null,
79
+ };
80
+ }
81
+
82
+ const fiber = findCurrentFiberUsingSlowPath(closestInstance);
83
+ const fiberHierarchy = getOwnerHierarchy(fiber);
84
+ const instance = lastNonHostInstance(fiberHierarchy);
85
+ const hierarchy = createHierarchy(fiberHierarchy);
86
+ const props = getHostProps(instance);
87
+ const source = instance._debugSource;
88
+ const selection = fiberHierarchy.indexOf(instance);
89
+
90
+ return {
91
+ hierarchy,
92
+ props,
93
+ selection,
94
+ source,
95
+ };
96
+ };
97
+} else {
98
+ getInspectorDataForViewTag = () => {
99
+ invariant(
100
+ false,
101
+ 'getInspectorDataForViewTag() is not available in production',
102
+ );
103
+ };
104
+}
105
+
106
+module.exports = {
107
+ getInspectorDataForViewTag,
108
+};
src/renderers/native-rt/ReactNativeRTFiberRenderer.js
new
+186
@@ -0,0 +1,186 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTFiberRenderer
8
+ * @flow
9
+ */
10
+
11
+'use strict';
12
+
13
+const ReactFiberReconciler = require('ReactFiberReconciler');
14
+const ReactNativeRTComponentTree = require('ReactNativeRTComponentTree');
15
+const ReactNativeRTTagHandles = require('ReactNativeRTTagHandles');
16
+const RTManager = require('RTManager');
17
+
18
+const emptyObject = require('fbjs/lib/emptyObject');
19
+const invariant = require('fbjs/lib/invariant');
20
+
21
+export type Container = number;
22
+export type Instance = number;
23
+export type Props = Object;
24
+export type TextInstance = number;
25
+
26
+const {precacheFiberNode, updateFiberProps} = ReactNativeRTComponentTree;
27
+
28
+const NativeRTRenderer = ReactFiberReconciler({
29
+ appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
30
+ RTManager.appendChild(parentInstance, child);
31
+ },
32
+
33
+ appendChildToContainer(
34
+ parentInstance: Container,
35
+ child: Instance | TextInstance,
36
+ ): void {
37
+ RTManager.appendChild(parentInstance, child);
38
+ },
39
+
40
+ appendInitialChild(
41
+ parentInstance: Instance,
42
+ child: Instance | TextInstance,
43
+ ): void {
44
+ RTManager.appendChildToDetachedParent(parentInstance, child);
45
+ },
46
+
47
+ commitTextUpdate(
48
+ textInstance: TextInstance,
49
+ oldText: string,
50
+ newText: string,
51
+ ): void {
52
+ invariant(false, 'Text components are not yet supported.');
53
+ },
54
+
55
+ commitMount(
56
+ instance: Instance,
57
+ type: string,
58
+ newProps: Props,
59
+ internalInstanceHandle: Object,
60
+ ): void {
61
+ // Noop
62
+ },
63
+
64
+ commitUpdate(
65
+ instance: Instance,
66
+ updatePayload: Object,
67
+ type: string,
68
+ oldProps: Props,
69
+ newProps: Props,
70
+ internalInstanceHandle: Object,
71
+ ): void {
72
+ updateFiberProps(instance, newProps);
73
+ RTManager.updateNode(instance, newProps);
74
+ },
75
+
76
+ createInstance(
77
+ type: string,
78
+ props: Props,
79
+ rootContainerInstance: Container,
80
+ hostContext: {},
81
+ internalInstanceHandle: Object,
82
+ ): Instance {
83
+ const tag = ReactNativeRTTagHandles.allocateTag();
84
+ RTManager.createNode(tag, type, props);
85
+ precacheFiberNode(internalInstanceHandle, tag);
86
+ updateFiberProps(tag, props);
87
+ return tag;
88
+ },
89
+
90
+ createTextInstance(
91
+ text: string,
92
+ rootContainerInstance: Container,
93
+ hostContext: {},
94
+ internalInstanceHandle: Object,
95
+ ): TextInstance {
96
+ invariant(false, 'Text components are not supported for now.');
97
+ },
98
+
99
+ finalizeInitialChildren(
100
+ parentInstance: Instance,
101
+ type: string,
102
+ props: Props,
103
+ rootContainerInstance: Container,
104
+ ): boolean {
105
+ return false;
106
+ },
107
+
108
+ getRootHostContext(): {} {
109
+ return emptyObject;
110
+ },
111
+
112
+ getChildHostContext(): {} {
113
+ return emptyObject;
114
+ },
115
+
116
+ getPublicInstance(instance) {
117
+ return instance;
118
+ },
119
+
120
+ insertBefore(
121
+ parentInstance: Instance,
122
+ child: Instance | TextInstance,
123
+ beforeChild: Instance | TextInstance,
124
+ ): void {
125
+ RTManager.prependChild(child, beforeChild);
126
+ },
127
+
128
+ insertInContainerBefore(
129
+ parentInstance: Container,
130
+ child: Instance | TextInstance,
131
+ beforeChild: Instance | TextInstance,
132
+ ): void {
133
+ RTManager.prependChild(child, beforeChild);
134
+ },
135
+
136
+ prepareForCommit(): void {
137
+ RTManager.beginUpdates();
138
+ },
139
+
140
+ prepareUpdate(
141
+ instance: Instance,
142
+ type: string,
143
+ oldProps: Props,
144
+ newProps: Props,
145
+ rootContainerInstance: Container,
146
+ hostContext: {},
147
+ ): null | Object {
148
+ return emptyObject;
149
+ },
150
+
151
+ removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
152
+ // TODO: recursively uncache, by traversing fibers, this will currently leak
153
+ RTManager.deleteChild(child);
154
+ },
155
+
156
+ removeChildFromContainer(
157
+ parentInstance: Container,
158
+ child: Instance | TextInstance,
159
+ ): void {
160
+ // TODO: recursively uncache, by traversing fibers, this will currently leak
161
+ RTManager.deleteChild(child);
162
+ },
163
+
164
+ resetAfterCommit(): void {
165
+ RTManager.completeUpdates();
166
+ },
167
+
168
+ resetTextContent(instance: Instance): void {
169
+ // Noop
170
+ },
171
+
172
+ shouldDeprioritizeSubtree(type: string, props: Props): boolean {
173
+ return false;
174
+ },
175
+
176
+ scheduleDeferredCallback: global.requestIdleCallback,
177
+
178
+ shouldSetTextContent(type: string, props: Props): boolean {
179
+ // TODO: Figure out when we should allow text content.
180
+ return false;
181
+ },
182
+
183
+ useSyncScheduling: true,
184
+});
185
+
186
+module.exports = NativeRTRenderer;
src/renderers/native-rt/ReactNativeRTTagHandles.js
new
+53
@@ -0,0 +1,53 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTTagHandles
8
+ * @flow
9
+ */
10
+'use strict';
11
+
12
+var invariant = require('fbjs/lib/invariant');
13
+
14
+/**
15
+ * Keeps track of allocating and associating native "tags" which are numeric,
16
+ * unique view IDs. All the native tags are negative numbers, to avoid
17
+ * collisions, but in the JS we keep track of them as positive integers to store
18
+ * them effectively in Arrays. So we must refer to them as "inverses" of the
19
+ * native tags (that are * normally negative).
20
+ *
21
+ * It *must* be the case that every `rootNodeID` always maps to the exact same
22
+ * `tag` forever. The easiest way to accomplish this is to never delete
23
+ * anything from this table.
24
+ * Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
25
+ * unmount a component with a `rootNodeID`, then mount a new one in its place,
26
+ */
27
+var INITIAL_TAG_COUNT = 1;
28
+var ReactNativeRTTagHandles = {
29
+ tagsStartAt: INITIAL_TAG_COUNT,
30
+ tagCount: INITIAL_TAG_COUNT,
31
+
32
+ allocateTag: function(): number {
33
+ // Skip over root IDs as those are reserved for native
34
+ var tag = ReactNativeRTTagHandles.tagCount;
35
+ ReactNativeRTTagHandles.tagCount++;
36
+ return tag;
37
+ },
38
+
39
+ assertRootTag: function(tag: number): void {
40
+ invariant(
41
+ ReactNativeRTTagHandles.reactTagIsNativeID(tag),
42
+ 'Expect a native root tag, instead got %s',
43
+ tag,
44
+ );
45
+ },
46
+
47
+ reactTagIsNativeID: function(reactTag: number): boolean {
48
+ // We reserve all tags that are 1 mod 10 for native view creation
49
+ return reactTag % 10 === 1;
50
+ },
51
+};
52
+
53
+module.exports = ReactNativeRTTagHandles;
src/renderers/native-rt/ReactNativeRTTypes.js
new
+24
@@ -0,0 +1,24 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @providesModule ReactNativeRTTypes
8
+ * @flow
9
+ */
10
+'use strict';
11
+
12
+/**
13
+ * Flat RT renderer bundles are too big for Flow to parse efficiently.
14
+ * Provide minimal Flow typing for the high-level RN API and call it a day.
15
+ */
16
+export type ReactNativeRTType = {
17
+ render(
18
+ element: React$Element<any>,
19
+ containerTag: any,
20
+ callback: ?Function,
21
+ ): any,
22
+ unmountComponentAtNode(containerTag: number): any,
23
+ unstable_batchedUpdates: any, // TODO (bvaughn) Add types
24
+};
src/renderers/native-rt/__tests__/ReactNativeRT-test.js
new
+46
@@ -0,0 +1,46 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails react-core
8
+ */
9
+
10
+'use strict';
11
+
12
+var React;
13
+var ReactNativeRT;
14
+var RTManager;
15
+
16
+describe('ReactNativeRT', () => {
17
+ beforeEach(() => {
18
+ jest.resetModules();
19
+
20
+ React = require('react');
21
+ ReactNativeRT = require('ReactNativeRTFiberEntry');
22
+ RTManager = require('RTManager');
23
+ });
24
+
25
+ it('should be able to create and render a native component', () => {
26
+ ReactNativeRT.render(<rt-box foo="test" />, 1);
27
+ expect(RTManager.createNode).toBeCalled();
28
+ expect(RTManager.appendChild).toBeCalled();
29
+ expect(RTManager.updateNode).not.toBeCalled();
30
+ });
31
+
32
+ it('should be able to create and update a native component', () => {
33
+ ReactNativeRT.render(<rt-box foo="foo" />, 11);
34
+
35
+ expect(RTManager.createNode.mock.calls.length).toBe(1);
36
+ expect(RTManager.createNode).toBeCalledWith(1, 'rt-box', {foo: 'foo'});
37
+ expect(RTManager.appendChild.mock.calls.length).toBe(1);
38
+ expect(RTManager.updateNode).not.toBeCalled();
39
+
40
+ ReactNativeRT.render(<rt-box foo="bar" />, 11);
41
+
42
+ expect(RTManager.createNode.mock.calls.length).toBe(1);
43
+ expect(RTManager.appendChild.mock.calls.length).toBe(1);
44
+ expect(RTManager.updateNode).toBeCalledWith(1, {foo: 'bar'});
45
+ });
46
+});