@samitouri / QOS-React-2 / commits / 36a2afccc5

[CS] Split Host Config Out into a Mutable or Immutable Mode (#11213)

* CS renderer Because we didn't have enough RN experiments. I want to add one more. * Split out hydration from the host config object This makes it easier to do feature detection on the configuration. * Move mutation host config to separate optional object * Refs and life-cycles should happen even in immutable mode * Unmount components even in non-mutation mode This is the same as committing deletions but instead of finding host components to delete, it only invokes componentWillUnmount and detaching of refs. * Add persistent updates API This mode will use a clone based API instead of mutating host instances. Needs implementation still. It's awkward that there can be more than one child inserted into the root. So we need a new API to create a "root" instance so that we can update it atomically. Alternatively we could keep the mutable API for containers and assume that most use cases would only have a single root. * Package up CS renderer * Fix reconciler package fixture

Sebastian Markbåge committed Oct 13, 2017 at 14:29 UTC 36a2afccc550ee958d33963ce27f1b13ca601159
19 files changed +1407 -987
fixtures/reconciler/index.js
+33 -31
@@ -115,28 +115,12 @@ var NoopRenderer = ReactFiberReconciler({
115 return UPDATE_SIGNAL;
116 },
117
118 - commitMount(instance: Instance, type: string, newProps: Props): void {
119 - // Noop
120 - },
121 -
122 - commitUpdate(
123 - instance: Instance,
124 - updatePayload: Object,
125 - type: string,
126 - oldProps: Props,
127 - newProps: Props
128 - ): void {
129 - instance.prop = newProps.prop;
130 - },
131 -
118 shouldSetTextContent(type: string, props: Props): boolean {
119 return (
120 typeof props.children === 'string' || typeof props.children === 'number'
121 );
122 },
123
138 - resetTextContent(instance: Instance): void {},
139 -
124 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
125 return !!props.hidden;
126 },
@@ -155,21 +139,6 @@ var NoopRenderer = ReactFiberReconciler({
139 return inst;
140 },
141
158 - commitTextUpdate(
159 - textInstance: TextInstance,
160 - oldText: string,
161 - newText: string
162 - ): void {
163 - textInstance.text = newText;
164 - },
165 -
166 - appendChild: appendChild,
167 - appendChildToContainer: appendChild,
168 - insertBefore: insertBefore,
169 - insertInContainerBefore: insertBefore,
170 - removeChild: removeChild,
171 - removeChildFromContainer: removeChild,
172 -
142 scheduleDeferredCallback(callback) {
143 if (scheduledCallback) {
144 throw new Error(
@@ -183,6 +152,39 @@ var NoopRenderer = ReactFiberReconciler({
152 prepareForCommit(): void {},
153
154 resetAfterCommit(): void {},
155 +
156 + mutation: {
157 + commitMount(instance: Instance, type: string, newProps: Props): void {
158 + // Noop
159 + },
160 +
161 + commitUpdate(
162 + instance: Instance,
163 + updatePayload: Object,
164 + type: string,
165 + oldProps: Props,
166 + newProps: Props
167 + ): void {
168 + instance.prop = newProps.prop;
169 + },
170 +
171 + commitTextUpdate(
172 + textInstance: TextInstance,
173 + oldText: string,
174 + newText: string
175 + ): void {
176 + textInstance.text = newText;
177 + },
178 +
179 + appendChild: appendChild,
180 + appendChildToContainer: appendChild,
181 + insertBefore: insertBefore,
182 + insertInContainerBefore: insertBefore,
183 + removeChild: removeChild,
184 + removeChildFromContainer: removeChild,
185 +
186 + resetTextContent(instance: Instance): void {},
187 + },
188 });
189
190 var rootContainers = new Map();
scripts/rollup/build.js
+5
@@ -21,6 +21,7 @@ const Stats = require('./stats');
21 const syncReactDom = require('./sync').syncReactDom;
22 const syncReactNative = require('./sync').syncReactNative;
23 const syncReactNativeRT = require('./sync').syncReactNativeRT;
24 +const syncReactNativeCS = require('./sync').syncReactNativeCS;
25 const Packaging = require('./packaging');
26 const Header = require('./header');
27 const closure = require('rollup-plugin-closure-compiler-js');
@@ -571,6 +572,7 @@ rimraf('build', () => {
572 Packaging.createFacebookWWWBuild,
573 Packaging.createReactNativeBuild,
574 Packaging.createReactNativeRTBuild,
575 + Packaging.createReactNativeCSBuild,
576 ];
577 for (const bundle of Bundles.bundles) {
578 tasks.push(
@@ -591,6 +593,9 @@ rimraf('build', () => {
593 tasks.push(() =>
594 syncReactNativeRT(join('build', 'react-native-rt'), syncFbsource)
595 );
596 + tasks.push(() =>
597 + syncReactNativeCS(join('build', 'react-native-cs'), syncFbsource)
598 + );
599 } else if (syncWww) {
600 tasks.push(() => syncReactDom(join('build', 'facebook-www'), syncWww));
601 }
scripts/rollup/bundles.js
+27 -1
@@ -298,7 +298,7 @@ const bundles = [
298 useFiber: true,
299 },
300
301 - /******* React Native *******/
301 + /******* React Native RT *******/
302 {
303 babelOpts: babelOptsReact,
304 bundleTypes: [RN_DEV, RN_PROD],
@@ -332,6 +332,32 @@ const bundles = [
332 useFiber: true,
333 },
334
335 + /******* React Native CS *******/
336 + {
337 + babelOpts: babelOptsReact,
338 + bundleTypes: [RN_DEV, RN_PROD],
339 + config: {
340 + destDir: 'build/',
341 + moduleName: 'ReactNativeCSFiber',
342 + sourceMap: false,
343 + },
344 + entry: 'src/renderers/native-cs/ReactNativeCSFiberEntry',
345 + externals: ['prop-types/checkPropTypes'],
346 + hasteName: 'ReactNativeCSFiber',
347 + isRenderer: true,
348 + label: 'native-cs-fiber',
349 + manglePropertiesOnProd: false,
350 + name: 'react-native-cs-renderer',
351 + paths: [
352 + 'src/renderers/native-cs/**/*.js',
353 + 'src/renderers/shared/**/*.js',
354 +
355 + 'src/ReactVersion.js',
356 + 'src/shared/**/*.js',
357 + ],
358 + useFiber: true,
359 + },
360 +
361 /******* React Test Renderer *******/
362 {
363 babelOpts: babelOptsReact,
scripts/rollup/packaging.js
+25
@@ -33,6 +33,11 @@ const reactNativeRTSrcDependencies = [
33 'src/renderers/native-rt/ReactNativeRTTypes.js',
34 ];
35
36 +// these files need to be copied to the react-native-cs build
37 +const reactNativeCSSrcDependencies = [
38 + 'src/renderers/native-cs/ReactNativeCSTypes.js',
39 +];
40 +
41 function getPackageName(name) {
42 if (name.indexOf('/') !== -1) {
43 return name.split('/')[0];
@@ -81,6 +86,25 @@ function createReactNativeRTBuild() {
86 return Promise.all(promises);
87 }
88
89 +function createReactNativeCSBuild() {
90 + // create the react-native-cs folder for FB bundles
91 + fs.mkdirSync(join('build', 'react-native-cs'));
92 + // create the react-native-cs shims folder for FB shims
93 + fs.mkdirSync(join('build', 'react-native-cs', 'shims'));
94 +
95 + const to = join('build', 'react-native-cs', 'shims');
96 +
97 + let promises = [];
98 + // we also need to copy over some specific files from src
99 + // defined in reactNativeCSSrcDependencies
100 + for (const srcDependency of reactNativeCSSrcDependencies) {
101 + promises.push(
102 + asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency)))
103 + );
104 + }
105 + return Promise.all(promises);
106 +}
107 +
108 function createFacebookWWWBuild() {
109 // create the facebookWWW folder for FB bundles
110 fs.mkdirSync(join('build', facebookWWW));
@@ -191,4 +215,5 @@ module.exports = {
215 createFacebookWWWBuild,
216 createReactNativeBuild,
217 createReactNativeRTBuild,
218 + createReactNativeCSBuild,
219 };
scripts/rollup/results.json
+52 -44
@@ -25,28 +25,28 @@
25 "gzip": 6703
26 },
27 "react-dom.development.js (UMD_DEV)": {
28 - "size": 630418,
29 - "gzip": 145239
28 + "size": 625837,
29 + "gzip": 144320
30 },
31 "react-dom.production.min.js (UMD_PROD)": {
32 - "size": 101638,
33 - "gzip": 32128
32 + "size": 100866,
33 + "gzip": 31848
34 },
35 "react-dom.development.js (NODE_DEV)": {
36 - "size": 592683,
37 - "gzip": 136496
36 + "size": 588106,
37 + "gzip": 135609
38 },
39 "react-dom.production.min.js (NODE_PROD)": {
40 - "size": 106507,
41 - "gzip": 33538
40 + "size": 105343,
41 + "gzip": 33129
42 },
43 "ReactDOMFiber-dev.js (FB_DEV)": {
44 - "size": 589582,
45 - "gzip": 135915
44 + "size": 584995,
45 + "gzip": 135013
46 },
47 "ReactDOMFiber-prod.js (FB_PROD)": {
48 - "size": 419983,
49 - "gzip": 93676
48 + "size": 414881,
49 + "gzip": 92663
50 },
51 "react-dom-test-utils.development.js (NODE_DEV)": {
52 "size": 41660,
@@ -113,44 +113,44 @@
113 "gzip": 6214
114 },
115 "react-art.development.js (UMD_DEV)": {
116 - "size": 376896,
117 - "gzip": 83318
116 + "size": 372576,
117 + "gzip": 82452
118 },
119 "react-art.production.min.js (UMD_PROD)": {
120 - "size": 83700,
121 - "gzip": 25995
120 + "size": 83057,
121 + "gzip": 25828
122 },
123 "react-art.development.js (NODE_DEV)": {
124 - "size": 301201,
125 - "gzip": 64095
124 + "size": 296909,
125 + "gzip": 63228
126 },
127 "react-art.production.min.js (NODE_PROD)": {
128 - "size": 53531,
129 - "gzip": 16898
128 + "size": 52508,
129 + "gzip": 16427
130 },
131 "ReactARTFiber-dev.js (FB_DEV)": {
132 - "size": 300073,
133 - "gzip": 64131
132 + "size": 295771,
133 + "gzip": 63258
134 },
135 "ReactARTFiber-prod.js (FB_PROD)": {
136 - "size": 224585,
137 - "gzip": 46566
136 + "size": 219800,
137 + "gzip": 45628
138 },
139 "ReactNativeFiber-dev.js (RN_DEV)": {
140 - "size": 283095,
141 - "gzip": 49176
140 + "size": 279764,
141 + "gzip": 48665
142 },
143 "ReactNativeFiber-prod.js (RN_PROD)": {
144 - "size": 221913,
145 - "gzip": 38509
144 + "size": 218506,
145 + "gzip": 37873
146 },
147 "react-test-renderer.development.js (NODE_DEV)": {
148 - "size": 304971,
149 - "gzip": 64484
148 + "size": 300619,
149 + "gzip": 63624
150 },
151 "ReactTestRendererFiber-dev.js (FB_DEV)": {
152 - "size": 303833,
153 - "gzip": 64527
152 + "size": 299471,
153 + "gzip": 63657
154 },
155 "react-test-renderer-shallow.development.js (NODE_DEV)": {
156 "size": 9215,
@@ -161,8 +161,8 @@
161 "gzip": 2221
162 },
163 "react-noop-renderer.development.js (NODE_DEV)": {
164 - "size": 292571,
165 - "gzip": 61440
164 + "size": 288214,
165 + "gzip": 60549
166 },
167 "react-dom-server.development.js (UMD_DEV)": {
168 "size": 120897,
@@ -189,16 +189,16 @@
189 "gzip": 7520
190 },
191 "ReactNativeRTFiber-dev.js (RN_DEV)": {
192 - "size": 215098,
193 - "gzip": 36673
192 + "size": 211687,
193 + "gzip": 36145
194 },
195 "ReactNativeRTFiber-prod.js (RN_PROD)": {
196 - "size": 163687,
197 - "gzip": 27527
196 + "size": 160200,
197 + "gzip": 26880
198 },
199 "react-test-renderer.production.min.js (NODE_PROD)": {
200 - "size": 55085,
201 - "gzip": 17135
200 + "size": 54076,
201 + "gzip": 16776
202 },
203 "react-test-renderer-shallow.production.min.js (NODE_PROD)": {
204 "size": 4536,
@@ -209,12 +209,20 @@
209 "gzip": 4241
210 },
211 "react-reconciler.development.js (NODE_DEV)": {
212 - "size": 279927,
213 - "gzip": 58576
212 + "size": 275518,
213 + "gzip": 57698
214 },
215 "react-reconciler.production.min.js (NODE_PROD)": {
216 - "size": 38630,
217 - "gzip": 12165
216 + "size": 37979,
217 + "gzip": 11842
218 + },
219 + "ReactNativeCSFiber-dev.js (RN_DEV)": {
220 + "size": 204077,
221 + "gzip": 34318
222 + },
223 + "ReactNativeCSFiber-prod.js (RN_PROD)": {
224 + "size": 155097,
225 + "gzip": 25642
226 }
227 }
228 }
\ No newline at end of file
src/renderers/art/ReactARTFiberEntry.js
+54 -52
@@ -388,20 +388,6 @@ class Text extends React.Component {
388 /** ART Renderer */
389
390 const ARTRenderer = ReactFiberReconciler({
391 - appendChild(parentInstance, child) {
392 - if (child.parentNode === parentInstance) {
393 - child.eject();
394 - }
395 - child.inject(parentInstance);
396 - },
397 -
398 - appendChildToContainer(parentInstance, child) {
399 - if (child.parentNode === parentInstance) {
400 - child.eject();
401 - }
402 - child.inject(parentInstance);
403 - },
404 -
391 appendInitialChild(parentInstance, child) {
392 if (typeof child === 'string') {
393 // Noop for string children of Text (eg <Text>{'foo'}{'bar'}</Text>)
@@ -412,18 +398,6 @@ const ARTRenderer = ReactFiberReconciler({
398 child.inject(parentInstance);
399 },
400
415 - commitTextUpdate(textInstance, oldText, newText) {
416 - // Noop
417 - },
418 -
419 - commitMount(instance, type, newProps) {
420 - // Noop
421 - },
422 -
423 - commitUpdate(instance, updatePayload, type, oldProps, newProps) {
424 - instance._applyProps(instance, newProps, oldProps);
425 - },
426 -
401 createInstance(type, props, internalInstanceHandle) {
402 let instance;
403
@@ -470,22 +444,6 @@ const ARTRenderer = ReactFiberReconciler({
444 return instance;
445 },
446
473 - insertBefore(parentInstance, child, beforeChild) {
474 - invariant(
475 - child !== beforeChild,
476 - 'ReactART: Can not insert node before itself',
477 - );
478 - child.injectBefore(beforeChild);
479 - },
480 -
481 - insertInContainerBefore(parentInstance, child, beforeChild) {
482 - invariant(
483 - child !== beforeChild,
484 - 'ReactART: Can not insert node before itself',
485 - );
486 - child.injectBefore(beforeChild);
487 - },
488 -
447 prepareForCommit() {
448 // Noop
449 },
@@ -494,16 +452,6 @@ const ARTRenderer = ReactFiberReconciler({
452 return UPDATE_SIGNAL;
453 },
454
497 - removeChild(parentInstance, child) {
498 - destroyEventListeners(child);
499 - child.eject();
500 - },
501 -
502 - removeChildFromContainer(parentInstance, child) {
503 - destroyEventListeners(child);
504 - child.eject();
505 - },
506 -
455 resetAfterCommit() {
456 // Noop
457 },
@@ -535,6 +483,60 @@ const ARTRenderer = ReactFiberReconciler({
483 now: ReactDOMFrameScheduling.now,
484
485 useSyncScheduling: true,
486 +
487 + mutation: {
488 + appendChild(parentInstance, child) {
489 + if (child.parentNode === parentInstance) {
490 + child.eject();
491 + }
492 + child.inject(parentInstance);
493 + },
494 +
495 + appendChildToContainer(parentInstance, child) {
496 + if (child.parentNode === parentInstance) {
497 + child.eject();
498 + }
499 + child.inject(parentInstance);
500 + },
501 +
502 + insertBefore(parentInstance, child, beforeChild) {
503 + invariant(
504 + child !== beforeChild,
505 + 'ReactART: Can not insert node before itself',
506 + );
507 + child.injectBefore(beforeChild);
508 + },
509 +
510 + insertInContainerBefore(parentInstance, child, beforeChild) {
511 + invariant(
512 + child !== beforeChild,
513 + 'ReactART: Can not insert node before itself',
514 + );
515 + child.injectBefore(beforeChild);
516 + },
517 +
518 + removeChild(parentInstance, child) {
519 + destroyEventListeners(child);
520 + child.eject();
521 + },
522 +
523 + removeChildFromContainer(parentInstance, child) {
524 + destroyEventListeners(child);
525 + child.eject();
526 + },
527 +
528 + commitTextUpdate(textInstance, oldText, newText) {
529 + // Noop
530 + },
531 +
532 + commitMount(instance, type, newProps) {
533 + // Noop
534 + },
535 +
536 + commitUpdate(instance, updatePayload, type, oldProps, newProps) {
537 + instance._applyProps(instance, newProps, oldProps);
538 + },
539 + },
540 });
541
542 /** API */
src/renderers/dom/fiber/ReactDOMFiberEntry.js
+270 -260
@@ -317,34 +317,6 @@ var DOMRenderer = ReactFiberReconciler({
317 );
318 },
319
320 - commitMount(
321 - domElement: Instance,
322 - type: string,
323 - newProps: Props,
324 - internalInstanceHandle: Object,
325 - ): void {
326 - ((domElement: any):
327 - | HTMLButtonElement
328 - | HTMLInputElement
329 - | HTMLSelectElement
330 - | HTMLTextAreaElement).focus();
331 - },
332 -
333 - commitUpdate(
334 - domElement: Instance,
335 - updatePayload: Array<mixed>,
336 - type: string,
337 - oldProps: Props,
338 - newProps: Props,
339 - internalInstanceHandle: Object,
340 - ): void {
341 - // Update the props handle so that we know which props are the ones with
342 - // with current event handlers.
343 - updateFiberProps(domElement, newProps);
344 - // Apply the diff to the DOM node.
345 - updateProperties(domElement, updatePayload, type, oldProps, newProps);
346 - },
347 -
320 shouldSetTextContent(type: string, props: Props): boolean {
321 return (
322 type === 'textarea' ||
@@ -356,10 +328,6 @@ var DOMRenderer = ReactFiberReconciler({
328 );
329 },
330
359 - resetTextContent(domElement: Instance): void {
360 - domElement.textContent = '';
361 - },
362 -
331 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
332 return !!props.hidden;
333 },
@@ -379,245 +347,287 @@ var DOMRenderer = ReactFiberReconciler({
347 return textNode;
348 },
349
382 - commitTextUpdate(
383 - textInstance: TextInstance,
384 - oldText: string,
385 - newText: string,
386 - ): void {
387 - textInstance.nodeValue = newText;
388 - },
389 -
390 - appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
391 - parentInstance.appendChild(child);
392 - },
393 -
394 - appendChildToContainer(
395 - container: Container,
396 - child: Instance | TextInstance,
397 - ): void {
398 - if (container.nodeType === COMMENT_NODE) {
399 - (container.parentNode: any).insertBefore(child, container);
400 - } else {
401 - container.appendChild(child);
402 - }
403 - },
404 -
405 - insertBefore(
406 - parentInstance: Instance,
407 - child: Instance | TextInstance,
408 - beforeChild: Instance | TextInstance,
409 - ): void {
410 - parentInstance.insertBefore(child, beforeChild);
411 - },
412 -
413 - insertInContainerBefore(
414 - container: Container,
415 - child: Instance | TextInstance,
416 - beforeChild: Instance | TextInstance,
417 - ): void {
418 - if (container.nodeType === COMMENT_NODE) {
419 - (container.parentNode: any).insertBefore(child, beforeChild);
420 - } else {
421 - container.insertBefore(child, beforeChild);
422 - }
423 - },
424 -
425 - removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
426 - parentInstance.removeChild(child);
427 - },
428 -
429 - removeChildFromContainer(
430 - container: Container,
431 - child: Instance | TextInstance,
432 - ): void {
433 - if (container.nodeType === COMMENT_NODE) {
434 - (container.parentNode: any).removeChild(child);
435 - } else {
436 - container.removeChild(child);
437 - }
438 - },
439 -
350 now: ReactDOMFrameScheduling.now,
351
442 - canHydrateInstance(
443 - instance: Instance | TextInstance,
444 - type: string,
445 - props: Props,
446 - ): boolean {
447 - return (
448 - instance.nodeType === ELEMENT_NODE &&
449 - type.toLowerCase() === instance.nodeName.toLowerCase()
450 - );
451 - },
452 -
453 - canHydrateTextInstance(
454 - instance: Instance | TextInstance,
455 - text: string,
456 - ): boolean {
457 - if (text === '') {
458 - // Empty strings are not parsed by HTML so there won't be a correct match here.
459 - return false;
460 - }
461 - return instance.nodeType === TEXT_NODE;
462 - },
463 -
464 - getNextHydratableSibling(
465 - instance: Instance | TextInstance,
466 - ): null | Instance | TextInstance {
467 - let node = instance.nextSibling;
468 - // Skip non-hydratable nodes.
469 - while (
470 - node &&
471 - node.nodeType !== ELEMENT_NODE &&
472 - node.nodeType !== TEXT_NODE
352 + mutation: {
353 + commitMount(
354 + domElement: Instance,
355 + type: string,
356 + newProps: Props,
357 + internalInstanceHandle: Object,
358 + ): void {
359 + ((domElement: any):
360 + | HTMLButtonElement
361 + | HTMLInputElement
362 + | HTMLSelectElement
363 + | HTMLTextAreaElement).focus();
364 + },
365 +
366 + commitUpdate(
367 + domElement: Instance,
368 + updatePayload: Array<mixed>,
369 + type: string,
370 + oldProps: Props,
371 + newProps: Props,
372 + internalInstanceHandle: Object,
373 + ): void {
374 + // Update the props handle so that we know which props are the ones with
375 + // with current event handlers.
376 + updateFiberProps(domElement, newProps);
377 + // Apply the diff to the DOM node.
378 + updateProperties(domElement, updatePayload, type, oldProps, newProps);
379 + },
380 +
381 + resetTextContent(domElement: Instance): void {
382 + domElement.textContent = '';
383 + },
384 +
385 + commitTextUpdate(
386 + textInstance: TextInstance,
387 + oldText: string,
388 + newText: string,
389 + ): void {
390 + textInstance.nodeValue = newText;
391 + },
392 +
393 + appendChild(
394 + parentInstance: Instance,
395 + child: Instance | TextInstance,
396 + ): void {
397 + parentInstance.appendChild(child);
398 + },
399 +
400 + appendChildToContainer(
401 + container: Container,
402 + child: Instance | TextInstance,
403 + ): void {
404 + if (container.nodeType === COMMENT_NODE) {
405 + (container.parentNode: any).insertBefore(child, container);
406 + } else {
407 + container.appendChild(child);
408 + }
409 + },
410 +
411 + insertBefore(
412 + parentInstance: Instance,
413 + child: Instance | TextInstance,
414 + beforeChild: Instance | TextInstance,
415 + ): void {
416 + parentInstance.insertBefore(child, beforeChild);
417 + },
418 +
419 + insertInContainerBefore(
420 + container: Container,
421 + child: Instance | TextInstance,
422 + beforeChild: Instance | TextInstance,
423 + ): void {
424 + if (container.nodeType === COMMENT_NODE) {
425 + (container.parentNode: any).insertBefore(child, beforeChild);
426 + } else {
427 + container.insertBefore(child, beforeChild);
428 + }
429 + },
430 +
431 + removeChild(
432 + parentInstance: Instance,
433 + child: Instance | TextInstance,
434 + ): void {
435 + parentInstance.removeChild(child);
436 + },
437 +
438 + removeChildFromContainer(
439 + container: Container,
440 + child: Instance | TextInstance,
441 + ): void {
442 + if (container.nodeType === COMMENT_NODE) {
443 + (container.parentNode: any).removeChild(child);
444 + } else {
445 + container.removeChild(child);
446 + }
447 + },
448 + },
449 +
450 + hydration: {
451 + canHydrateInstance(
452 + instance: Instance | TextInstance,
453 + type: string,
454 + props: Props,
455 + ): boolean {
456 + return (
457 + instance.nodeType === ELEMENT_NODE &&
458 + type.toLowerCase() === instance.nodeName.toLowerCase()
459 + );
460 + },
461 +
462 + canHydrateTextInstance(
463 + instance: Instance | TextInstance,
464 + text: string,
465 + ): boolean {
466 + if (text === '') {
467 + // Empty strings are not parsed by HTML so there won't be a correct match here.
468 + return false;
469 + }
470 + return instance.nodeType === TEXT_NODE;
471 + },
472 +
473 + getNextHydratableSibling(
474 + instance: Instance | TextInstance,
475 + ): null | Instance | TextInstance {
476 + let node = instance.nextSibling;
477 + // Skip non-hydratable nodes.
478 + while (
479 + node &&
480 + node.nodeType !== ELEMENT_NODE &&
481 + node.nodeType !== TEXT_NODE
482 + ) {
483 + node = node.nextSibling;
484 + }
485 + return (node: any);
486 + },
487 +
488 + getFirstHydratableChild(
489 + parentInstance: Container | Instance,
490 + ): null | Instance | TextInstance {
491 + let next = parentInstance.firstChild;
492 + // Skip non-hydratable nodes.
493 + while (
494 + next &&
495 + next.nodeType !== ELEMENT_NODE &&
496 + next.nodeType !== TEXT_NODE
497 + ) {
498 + next = next.nextSibling;
499 + }
500 + return (next: any);
501 + },
502 +
503 + hydrateInstance(
504 + instance: Instance,
505 + type: string,
506 + props: Props,
507 + rootContainerInstance: Container,
508 + hostContext: HostContext,
509 + internalInstanceHandle: Object,
510 + ): null | Array<mixed> {
511 + precacheFiberNode(internalInstanceHandle, instance);
512 + // TODO: Possibly defer this until the commit phase where all the events
513 + // get attached.
514 + updateFiberProps(instance, props);
515 + let parentNamespace: string;
516 + if (__DEV__) {
517 + const hostContextDev = ((hostContext: any): HostContextDev);
518 + parentNamespace = hostContextDev.namespace;
519 + } else {
520 + parentNamespace = ((hostContext: any): HostContextProd);
521 + }
522 + return diffHydratedProperties(
523 + instance,
524 + type,
525 + props,
526 + parentNamespace,
527 + rootContainerInstance,
528 + );
529 + },
530 +
531 + hydrateTextInstance(
532 + textInstance: TextInstance,
533 + text: string,
534 + internalInstanceHandle: Object,
535 + ): boolean {
536 + precacheFiberNode(internalInstanceHandle, textInstance);
537 + return diffHydratedText(textInstance, text);
538 + },
539 +
540 + didNotMatchHydratedContainerTextInstance(
541 + parentContainer: Container,
542 + textInstance: TextInstance,
543 + text: string,
544 ) {
474 - node = node.nextSibling;
475 - }
476 - return (node: any);
477 - },
478 -
479 - getFirstHydratableChild(
480 - parentInstance: Container | Instance,
481 - ): null | Instance | TextInstance {
482 - let next = parentInstance.firstChild;
483 - // Skip non-hydratable nodes.
484 - while (
485 - next &&
486 - next.nodeType !== ELEMENT_NODE &&
487 - next.nodeType !== TEXT_NODE
545 + if (__DEV__) {
546 + warnForUnmatchedText(textInstance, text);
547 + }
548 + },
549 +
550 + didNotMatchHydratedTextInstance(
551 + parentType: string,
552 + parentProps: Props,
553 + parentInstance: Instance,
554 + textInstance: TextInstance,
555 + text: string,
556 ) {
489 - next = next.nextSibling;
490 - }
491 - return (next: any);
492 - },
493 -
494 - hydrateInstance(
495 - instance: Instance,
496 - type: string,
497 - props: Props,
498 - rootContainerInstance: Container,
499 - hostContext: HostContext,
500 - internalInstanceHandle: Object,
501 - ): null | Array<mixed> {
502 - precacheFiberNode(internalInstanceHandle, instance);
503 - // TODO: Possibly defer this until the commit phase where all the events
504 - // get attached.
505 - updateFiberProps(instance, props);
506 - let parentNamespace: string;
507 - if (__DEV__) {
508 - const hostContextDev = ((hostContext: any): HostContextDev);
509 - parentNamespace = hostContextDev.namespace;
510 - } else {
511 - parentNamespace = ((hostContext: any): HostContextProd);
512 - }
513 - return diffHydratedProperties(
514 - instance,
515 - type,
516 - props,
517 - parentNamespace,
518 - rootContainerInstance,
519 - );
520 - },
521 -
522 - hydrateTextInstance(
523 - textInstance: TextInstance,
524 - text: string,
525 - internalInstanceHandle: Object,
526 - ): boolean {
527 - precacheFiberNode(internalInstanceHandle, textInstance);
528 - return diffHydratedText(textInstance, text);
529 - },
530 -
531 - didNotMatchHydratedContainerTextInstance(
532 - parentContainer: Container,
533 - textInstance: TextInstance,
534 - text: string,
535 - ) {
536 - if (__DEV__) {
537 - warnForUnmatchedText(textInstance, text);
538 - }
539 - },
540 -
541 - didNotMatchHydratedTextInstance(
542 - parentType: string,
543 - parentProps: Props,
544 - parentInstance: Instance,
545 - textInstance: TextInstance,
546 - text: string,
547 - ) {
548 - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
549 - warnForUnmatchedText(textInstance, text);
550 - }
551 - },
552 -
553 - didNotHydrateContainerInstance(
554 - parentContainer: Container,
555 - instance: Instance | TextInstance,
556 - ) {
557 - if (__DEV__) {
558 - if (instance.nodeType === 1) {
559 - warnForDeletedHydratableElement(parentContainer, (instance: any));
560 - } else {
561 - warnForDeletedHydratableText(parentContainer, (instance: any));
557 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
558 + warnForUnmatchedText(textInstance, text);
559 }
563 - }
564 - },
560 + },
561
566 - didNotHydrateInstance(
567 - parentType: string,
568 - parentProps: Props,
569 - parentInstance: Instance,
570 - instance: Instance | TextInstance,
571 - ) {
572 - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
573 - if (instance.nodeType === 1) {
574 - warnForDeletedHydratableElement(parentInstance, (instance: any));
575 - } else {
576 - warnForDeletedHydratableText(parentInstance, (instance: any));
562 + didNotHydrateContainerInstance(
563 + parentContainer: Container,
564 + instance: Instance | TextInstance,
565 + ) {
566 + if (__DEV__) {
567 + if (instance.nodeType === 1) {
568 + warnForDeletedHydratableElement(parentContainer, (instance: any));
569 + } else {
570 + warnForDeletedHydratableText(parentContainer, (instance: any));
571 + }
572 }
578 - }
579 - },
573 + },
574
581 - didNotFindHydratableContainerInstance(
582 - parentContainer: Container,
583 - type: string,
584 - props: Props,
585 - ) {
586 - if (__DEV__) {
587 - warnForInsertedHydratedElement(parentContainer, type, props);
588 - }
589 - },
575 + didNotHydrateInstance(
576 + parentType: string,
577 + parentProps: Props,
578 + parentInstance: Instance,
579 + instance: Instance | TextInstance,
580 + ) {
581 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
582 + if (instance.nodeType === 1) {
583 + warnForDeletedHydratableElement(parentInstance, (instance: any));
584 + } else {
585 + warnForDeletedHydratableText(parentInstance, (instance: any));
586 + }
587 + }
588 + },
589
591 - didNotFindHydratableContainerTextInstance(
592 - parentContainer: Container,
593 - text: string,
594 - ) {
595 - if (__DEV__) {
596 - warnForInsertedHydratedText(parentContainer, text);
597 - }
598 - },
590 + didNotFindHydratableContainerInstance(
591 + parentContainer: Container,
592 + type: string,
593 + props: Props,
594 + ) {
595 + if (__DEV__) {
596 + warnForInsertedHydratedElement(parentContainer, type, props);
597 + }
598 + },
599
600 - didNotFindHydratableInstance(
601 - parentType: string,
602 - parentProps: Props,
603 - parentInstance: Instance,
604 - type: string,
605 - props: Props,
606 - ) {
607 - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
608 - warnForInsertedHydratedElement(parentInstance, type, props);
609 - }
610 - },
600 + didNotFindHydratableContainerTextInstance(
601 + parentContainer: Container,
602 + text: string,
603 + ) {
604 + if (__DEV__) {
605 + warnForInsertedHydratedText(parentContainer, text);
606 + }
607 + },
608 +
609 + didNotFindHydratableInstance(
610 + parentType: string,
611 + parentProps: Props,
612 + parentInstance: Instance,
613 + type: string,
614 + props: Props,
615 + ) {
616 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
617 + warnForInsertedHydratedElement(parentInstance, type, props);
618 + }
619 + },
620
612 - didNotFindHydratableTextInstance(
613 - parentType: string,
614 - parentProps: Props,
615 - parentInstance: Instance,
616 - text: string,
617 - ) {
618 - if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
619 - warnForInsertedHydratedText(parentInstance, text);
620 - }
621 + didNotFindHydratableTextInstance(
622 + parentType: string,
623 + parentProps: Props,
624 + parentInstance: Instance,
625 + text: string,
626 + ) {
627 + if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
628 + warnForInsertedHydratedText(parentInstance, text);
629 + }
630 + },
631 },
632
633 scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
src/renderers/native-cs/ReactNativeCSFiberEntry.js new
+228
@@ -0,0 +1,228 @@
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 ReactNativeCSFiberEntry
8 + * @flow
9 + */
10 +
11 +'use strict';
12 +
13 +const ReactGenericBatching = require('ReactGenericBatching');
14 +const ReactVersion = require('ReactVersion');
15 +
16 +const {injectInternals} = require('ReactFiberDevToolsHook');
17 +
18 +import type {ReactNativeCSType} from 'ReactNativeCSTypes';
19 +
20 +const ReactFiberReconciler = require('ReactFiberReconciler');
21 +
22 +const emptyObject = require('fbjs/lib/emptyObject');
23 +
24 +export type Container = number;
25 +export type Instance = number;
26 +export type Props = Object;
27 +export type TextInstance = number;
28 +
29 +function processProps(instance: number, props: Props): Object {
30 + const propsPayload = {};
31 + for (var key in props) {
32 + if (key === 'children') {
33 + // Skip special case.
34 + continue;
35 + }
36 + var value = props[key];
37 + if (typeof value === 'function') {
38 + value = {
39 + style: 'rt-event',
40 + event: key,
41 + tag: instance,
42 + };
43 + }
44 + propsPayload[key] = value;
45 + }
46 + return propsPayload;
47 +}
48 +
49 +function arePropsEqual(oldProps: Props, newProps: Props): boolean {
50 + var key;
51 + for (key in newProps) {
52 + if (key === 'children') {
53 + // Skip special case.
54 + continue;
55 + }
56 + if (newProps[key] !== oldProps[key]) {
57 + return false;
58 + }
59 + }
60 + for (key in oldProps) {
61 + if (key === 'children') {
62 + // Skip special case.
63 + continue;
64 + }
65 + if (!(key in newProps)) {
66 + return false;
67 + }
68 + }
69 + return true;
70 +}
71 +
72 +const ReactNativeCSFiberRenderer = ReactFiberReconciler({
73 + appendInitialChild(
74 + parentInstance: Instance,
75 + child: Instance | TextInstance,
76 + ): void {},
77 +
78 + createInstance(
79 + type: string,
80 + props: Props,
81 + rootContainerInstance: Container,
82 + hostContext: {},
83 + internalInstanceHandle: Object,
84 + ): Instance {
85 + return 0;
86 + },
87 +
88 + createTextInstance(
89 + text: string,
90 + rootContainerInstance: Container,
91 + hostContext: {},
92 + internalInstanceHandle: Object,
93 + ): TextInstance {
94 + return 0;
95 + },
96 +
97 + finalizeInitialChildren(
98 + parentInstance: Instance,
99 + type: string,
100 + props: Props,
101 + rootContainerInstance: Container,
102 + ): boolean {
103 + return false;
104 + },
105 +
106 + getRootHostContext(): {} {
107 + return emptyObject;
108 + },
109 +
110 + getChildHostContext(): {} {
111 + return emptyObject;
112 + },
113 +
114 + getPublicInstance(instance) {
115 + return instance;
116 + },
117 +
118 + prepareForCommit(): void {},
119 +
120 + prepareUpdate(
121 + instance: Instance,
122 + type: string,
123 + oldProps: Props,
124 + newProps: Props,
125 + rootContainerInstance: Container,
126 + hostContext: {},
127 + ): null | Object {
128 + if (arePropsEqual(oldProps, newProps)) {
129 + return null;
130 + }
131 + return processProps(instance, newProps);
132 + },
133 +
134 + resetAfterCommit(): void {},
135 +
136 + shouldDeprioritizeSubtree(type: string, props: Props): boolean {
137 + return false;
138 + },
139 +
140 + scheduleDeferredCallback: global.requestIdleCallback,
141 +
142 + shouldSetTextContent(type: string, props: Props): boolean {
143 + // TODO: Figure out when we should allow text content.
144 + return false;
145 + },
146 +
147 + useSyncScheduling: true,
148 +
149 + now(): number {
150 + // TODO: Enable expiration by implementing this method.
151 + return 0;
152 + },
153 +
154 + persistence: {
155 + cloneInstance(
156 + instance: Instance,
157 + updatePayload: Object,
158 + type: string,
159 + oldProps: Props,
160 + newProps: Props,
161 + internalInstanceHandle: Object,
162 + keepChildren: boolean,
163 + ): Instance {
164 + return 0;
165 + },
166 + tryToReuseInstance(
167 + instance: Instance,
168 + updatePayload: Object,
169 + type: string,
170 + oldProps: Props,
171 + newProps: Props,
172 + internalInstanceHandle: Object,
173 + keepChildren: boolean,
174 + ): Instance {
175 + return 0;
176 + },
177 +
178 + createRootInstance(
179 + rootContainerInstance: Container,
180 + hostContext: {},
181 + ): Instance {
182 + return 123;
183 + },
184 + commitRootInstance(rootInstance: Instance): void {},
185 + },
186 +});
187 +
188 +const roots = new Map();
189 +
190 +const ReactNativeCSFiber: ReactNativeCSType = {
191 + render(element: React$Element<any>, containerTag: any, callback: ?Function) {
192 + let root = roots.get(containerTag);
193 +
194 + if (!root) {
195 + // TODO (bvaughn): If we decide to keep the wrapper component,
196 + // We could create a wrapper for containerTag as well to reduce special casing.
197 + root = ReactNativeCSFiberRenderer.createContainer(containerTag);
198 + roots.set(containerTag, root);
199 + }
200 + ReactNativeCSFiberRenderer.updateContainer(element, root, null, callback);
201 +
202 + return ReactNativeCSFiberRenderer.getPublicRootInstance(root);
203 + },
204 +
205 + unmountComponentAtNode(containerTag: number) {
206 + const root = roots.get(containerTag);
207 + if (root) {
208 + // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
209 + ReactNativeCSFiberRenderer.updateContainer(null, root, null, () => {
210 + roots.delete(containerTag);
211 + });
212 + }
213 + },
214 +
215 + unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
216 +
217 + flushSync: ReactNativeCSFiberRenderer.flushSync,
218 +};
219 +
220 +injectInternals({
221 + findHostInstanceByFiber: ReactNativeCSFiberRenderer.findHostInstance,
222 + // This is an enum because we may add more (e.g. profiler build)
223 + bundleType: __DEV__ ? 1 : 0,
224 + version: ReactVersion,
225 + rendererPackageName: 'react-native-cs',
226 +});
227 +
228 +module.exports = ReactNativeCSFiber;
src/renderers/native-cs/ReactNativeCSTypes.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 ReactNativeCSTypes
8 + * @flow
9 + */
10 +'use strict';
11 +
12 +/**
13 + * Flat CS 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 ReactNativeCSType = {
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-cs/__tests__/ReactNativeCS-test.js new
+27
@@ -0,0 +1,27 @@
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 ReactNativeCS;
14 +
15 +describe('ReactNativeCS', () => {
16 + beforeEach(() => {
17 + jest.resetModules();
18 +
19 + React = require('react');
20 + ReactNativeCS = require('ReactNativeCSFiberEntry');
21 + });
22 +
23 + it('should be able to create and render a native component', () => {
24 + const RCTView = 'View';
25 + ReactNativeCS.render(<RCTView foo="test" />, 1);
26 + });
27 +});
src/renderers/native-rt/ReactNativeRTFiberRenderer.js
+81 -73
@@ -69,17 +69,6 @@ function arePropsEqual(oldProps: Props, newProps: Props): boolean {
69 }
70
71 const NativeRTRenderer = ReactFiberReconciler({
72 - appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
73 - RTManager.appendChild(parentInstance, child);
74 - },
75 -
76 - appendChildToContainer(
77 - parentInstance: Container,
78 - child: Instance | TextInstance,
79 - ): void {
80 - RTManager.appendChildToContext(parentInstance, child);
81 - },
82 -
72 appendInitialChild(
73 parentInstance: Instance,
74 child: Instance | TextInstance,
@@ -87,35 +76,6 @@ const NativeRTRenderer = ReactFiberReconciler({
76 RTManager.appendChild(parentInstance, child);
77 },
78
90 - commitTextUpdate(
91 - textInstance: TextInstance,
92 - oldText: string,
93 - newText: string,
94 - ): void {
95 - invariant(false, 'Text components are not yet supported.');
96 - },
97 -
98 - commitMount(
99 - instance: Instance,
100 - type: string,
101 - newProps: Props,
102 - internalInstanceHandle: Object,
103 - ): void {
104 - // Noop
105 - },
106 -
107 - commitUpdate(
108 - instance: Instance,
109 - updatePayload: Object,
110 - type: string,
111 - oldProps: Props,
112 - newProps: Props,
113 - internalInstanceHandle: Object,
114 - ): void {
115 - updateFiberProps(instance, newProps);
116 - RTManager.updateNode(instance, updatePayload);
117 - },
118 -
79 createInstance(
80 type: string,
81 props: Props,
@@ -160,22 +120,6 @@ const NativeRTRenderer = ReactFiberReconciler({
120 return instance;
121 },
122
163 - insertBefore(
164 - parentInstance: Instance,
165 - child: Instance | TextInstance,
166 - beforeChild: Instance | TextInstance,
167 - ): void {
168 - RTManager.prependChild(child, beforeChild);
169 - },
170 -
171 - insertInContainerBefore(
172 - parentInstance: Container,
173 - child: Instance | TextInstance,
174 - beforeChild: Instance | TextInstance,
175 - ): void {
176 - RTManager.prependChild(child, beforeChild);
177 - },
178 -
123 prepareForCommit(): void {
124 RTManager.beginUpdates();
125 },
@@ -194,27 +138,10 @@ const NativeRTRenderer = ReactFiberReconciler({
138 return processProps(instance, newProps);
139 },
140
197 - removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
198 - // TODO: recursively uncache, by traversing fibers, this will currently leak
199 - RTManager.deleteChild(child);
200 - },
201 -
202 - removeChildFromContainer(
203 - parentInstance: Container,
204 - child: Instance | TextInstance,
205 - ): void {
206 - // TODO: recursively uncache, by traversing fibers, this will currently leak
207 - RTManager.deleteChild(child);
208 - },
209 -
141 resetAfterCommit(): void {
142 RTManager.completeUpdates();
143 },
144
214 - resetTextContent(instance: Instance): void {
215 - // Noop
216 - },
217 -
145 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
146 return false;
147 },
@@ -232,6 +159,87 @@ const NativeRTRenderer = ReactFiberReconciler({
159 // TODO: Enable expiration by implementing this method.
160 return 0;
161 },
162 +
163 + mutation: {
164 + appendChild(
165 + parentInstance: Instance,
166 + child: Instance | TextInstance,
167 + ): void {
168 + RTManager.appendChild(parentInstance, child);
169 + },
170 +
171 + appendChildToContainer(
172 + parentInstance: Container,
173 + child: Instance | TextInstance,
174 + ): void {
175 + RTManager.appendChildToContext(parentInstance, child);
176 + },
177 +
178 + commitTextUpdate(
179 + textInstance: TextInstance,
180 + oldText: string,
181 + newText: string,
182 + ): void {
183 + invariant(false, 'Text components are not yet supported.');
184 + },
185 +
186 + commitMount(
187 + instance: Instance,
188 + type: string,
189 + newProps: Props,
190 + internalInstanceHandle: Object,
191 + ): void {
192 + // Noop
193 + },
194 +
195 + commitUpdate(
196 + instance: Instance,
197 + updatePayload: Object,
198 + type: string,
199 + oldProps: Props,
200 + newProps: Props,
201 + internalInstanceHandle: Object,
202 + ): void {
203 + updateFiberProps(instance, newProps);
204 + RTManager.updateNode(instance, updatePayload);
205 + },
206 +
207 + insertBefore(
208 + parentInstance: Instance,
209 + child: Instance | TextInstance,
210 + beforeChild: Instance | TextInstance,
211 + ): void {
212 + RTManager.prependChild(child, beforeChild);
213 + },
214 +
215 + insertInContainerBefore(
216 + parentInstance: Container,
217 + child: Instance | TextInstance,
218 + beforeChild: Instance | TextInstance,
219 + ): void {
220 + RTManager.prependChild(child, beforeChild);
221 + },
222 +
223 + removeChild(
224 + parentInstance: Instance,
225 + child: Instance | TextInstance,
226 + ): void {
227 + // TODO: recursively uncache, by traversing fibers, this will currently leak
228 + RTManager.deleteChild(child);
229 + },
230 +
231 + removeChildFromContainer(
232 + parentInstance: Container,
233 + child: Instance | TextInstance,
234 + ): void {
235 + // TODO: recursively uncache, by traversing fibers, this will currently leak
236 + RTManager.deleteChild(child);
237 + },
238 +
239 + resetTextContent(instance: Instance): void {
240 + // Noop
241 + },
242 + },
243 });
244
245 module.exports = NativeRTRenderer;
src/renderers/native/ReactNativeFiberRenderer.js
+191 -183
@@ -51,48 +51,6 @@ function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
51 }
52
53 const NativeRenderer = ReactFiberReconciler({
54 - appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
55 - const childTag = typeof child === 'number' ? child : child._nativeTag;
56 - const children = parentInstance._children;
57 - const index = children.indexOf(child);
58 -
59 - if (index >= 0) {
60 - children.splice(index, 1);
61 - children.push(child);
62 -
63 - UIManager.manageChildren(
64 - parentInstance._nativeTag, // containerTag
65 - [index], // moveFromIndices
66 - [children.length - 1], // moveToIndices
67 - [], // addChildReactTags
68 - [], // addAtIndices
69 - [], // removeAtIndices
70 - );
71 - } else {
72 - children.push(child);
73 -
74 - UIManager.manageChildren(
75 - parentInstance._nativeTag, // containerTag
76 - [], // moveFromIndices
77 - [], // moveToIndices
78 - [childTag], // addChildReactTags
79 - [children.length - 1], // addAtIndices
80 - [], // removeAtIndices
81 - );
82 - }
83 - },
84 -
85 - appendChildToContainer(
86 - parentInstance: Container,
87 - child: Instance | TextInstance,
88 - ): void {
89 - const childTag = typeof child === 'number' ? child : child._nativeTag;
90 - UIManager.setChildren(
91 - parentInstance, // containerTag
92 - [childTag], // reactTags
93 - );
94 - },
95 -
54 appendInitialChild(
55 parentInstance: Instance,
56 child: Instance | TextInstance,
@@ -100,57 +58,6 @@ const NativeRenderer = ReactFiberReconciler({
58 parentInstance._children.push(child);
59 },
60
103 - commitTextUpdate(
104 - textInstance: TextInstance,
105 - oldText: string,
106 - newText: string,
107 - ): void {
108 - UIManager.updateView(
109 - textInstance, // reactTag
110 - 'RCTRawText', // viewName
111 - {text: newText}, // props
112 - );
113 - },
114 -
115 - commitMount(
116 - instance: Instance,
117 - type: string,
118 - newProps: Props,
119 - internalInstanceHandle: Object,
120 - ): void {
121 - // Noop
122 - },
123 -
124 - commitUpdate(
125 - instance: Instance,
126 - updatePayloadTODO: Object,
127 - type: string,
128 - oldProps: Props,
129 - newProps: Props,
130 - internalInstanceHandle: Object,
131 - ): void {
132 - const viewConfig = instance.viewConfig;
133 -
134 - updateFiberProps(instance._nativeTag, newProps);
135 -
136 - const updatePayload = ReactNativeAttributePayload.diff(
137 - oldProps,
138 - newProps,
139 - viewConfig.validAttributes,
140 - );
141 -
142 - // Avoid the overhead of bridge calls if there's no update.
143 - // This is an expensive no-op for Android, and causes an unnecessary
144 - // view invalidation for certain components (eg RCTTextInput) on iOS.
145 - if (updatePayload != null) {
146 - UIManager.updateView(
147 - instance._nativeTag, // reactTag
148 - viewConfig.uiViewClassName, // viewName
149 - updatePayload, // props
150 - );
151 - }
152 - },
153 -
61 createInstance(
62 type: string,
63 props: Props,
@@ -251,60 +158,6 @@ const NativeRenderer = ReactFiberReconciler({
158 return instance;
159 },
160
254 - insertBefore(
255 - parentInstance: Instance,
256 - child: Instance | TextInstance,
257 - beforeChild: Instance | TextInstance,
258 - ): void {
259 - const children = (parentInstance: any)._children;
260 - const index = children.indexOf(child);
261 -
262 - // Move existing child or add new child?
263 - if (index >= 0) {
264 - children.splice(index, 1);
265 - const beforeChildIndex = children.indexOf(beforeChild);
266 - children.splice(beforeChildIndex, 0, child);
267 -
268 - UIManager.manageChildren(
269 - (parentInstance: any)._nativeTag, // containerID
270 - [index], // moveFromIndices
271 - [beforeChildIndex], // moveToIndices
272 - [], // addChildReactTags
273 - [], // addAtIndices
274 - [], // removeAtIndices
275 - );
276 - } else {
277 - const beforeChildIndex = children.indexOf(beforeChild);
278 - children.splice(beforeChildIndex, 0, child);
279 -
280 - const childTag = typeof child === 'number' ? child : child._nativeTag;
281 -
282 - UIManager.manageChildren(
283 - (parentInstance: any)._nativeTag, // containerID
284 - [], // moveFromIndices
285 - [], // moveToIndices
286 - [childTag], // addChildReactTags
287 - [beforeChildIndex], // addAtIndices
288 - [], // removeAtIndices
289 - );
290 - }
291 - },
292 -
293 - insertInContainerBefore(
294 - parentInstance: Container,
295 - child: Instance | TextInstance,
296 - beforeChild: Instance | TextInstance,
297 - ): void {
298 - // TODO (bvaughn): Remove this check when...
299 - // We create a wrapper object for the container in ReactNative render()
300 - // Or we refactor to remove wrapper objects entirely.
301 - // For more info on pros/cons see PR #8560 description.
302 - invariant(
303 - typeof parentInstance !== 'number',
304 - 'Container does not support insertBefore operation',
305 - );
306 - },
307 -
161 prepareForCommit(): void {
162 // Noop
163 },
@@ -320,46 +173,10 @@ const NativeRenderer = ReactFiberReconciler({
173 return emptyObject;
174 },
175
323 - removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
324 - recursivelyUncacheFiberNode(child);
325 - const children = parentInstance._children;
326 - const index = children.indexOf(child);
327 -
328 - children.splice(index, 1);
329 -
330 - UIManager.manageChildren(
331 - parentInstance._nativeTag, // containerID
332 - [], // moveFromIndices
333 - [], // moveToIndices
334 - [], // addChildReactTags
335 - [], // addAtIndices
336 - [index], // removeAtIndices
337 - );
338 - },
339 -
340 - removeChildFromContainer(
341 - parentInstance: Container,
342 - child: Instance | TextInstance,
343 - ): void {
344 - recursivelyUncacheFiberNode(child);
345 - UIManager.manageChildren(
346 - parentInstance, // containerID
347 - [], // moveFromIndices
348 - [], // moveToIndices
349 - [], // addChildReactTags
350 - [], // addAtIndices
351 - [0], // removeAtIndices
352 - );
353 - },
354 -
176 resetAfterCommit(): void {
177 // Noop
178 },
179
359 - resetTextContent(instance: Instance): void {
360 - // Noop
361 - },
362 -
180 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
181 return false;
182 },
@@ -382,6 +199,197 @@ const NativeRenderer = ReactFiberReconciler({
199 // TODO: Enable expiration by implementing this method.
200 return 0;
201 },
202 +
203 + mutation: {
204 + appendChild(
205 + parentInstance: Instance,
206 + child: Instance | TextInstance,
207 + ): void {
208 + const childTag = typeof child === 'number' ? child : child._nativeTag;
209 + const children = parentInstance._children;
210 + const index = children.indexOf(child);
211 +
212 + if (index >= 0) {
213 + children.splice(index, 1);
214 + children.push(child);
215 +
216 + UIManager.manageChildren(
217 + parentInstance._nativeTag, // containerTag
218 + [index], // moveFromIndices
219 + [children.length - 1], // moveToIndices
220 + [], // addChildReactTags
221 + [], // addAtIndices
222 + [], // removeAtIndices
223 + );
224 + } else {
225 + children.push(child);
226 +
227 + UIManager.manageChildren(
228 + parentInstance._nativeTag, // containerTag
229 + [], // moveFromIndices
230 + [], // moveToIndices
231 + [childTag], // addChildReactTags
232 + [children.length - 1], // addAtIndices
233 + [], // removeAtIndices
234 + );
235 + }
236 + },
237 +
238 + appendChildToContainer(
239 + parentInstance: Container,
240 + child: Instance | TextInstance,
241 + ): void {
242 + const childTag = typeof child === 'number' ? child : child._nativeTag;
243 + UIManager.setChildren(
244 + parentInstance, // containerTag
245 + [childTag], // reactTags
246 + );
247 + },
248 +
249 + commitTextUpdate(
250 + textInstance: TextInstance,
251 + oldText: string,
252 + newText: string,
253 + ): void {
254 + UIManager.updateView(
255 + textInstance, // reactTag
256 + 'RCTRawText', // viewName
257 + {text: newText}, // props
258 + );
259 + },
260 +
261 + commitMount(
262 + instance: Instance,
263 + type: string,
264 + newProps: Props,
265 + internalInstanceHandle: Object,
266 + ): void {
267 + // Noop
268 + },
269 +
270 + commitUpdate(
271 + instance: Instance,
272 + updatePayloadTODO: Object,
273 + type: string,
274 + oldProps: Props,
275 + newProps: Props,
276 + internalInstanceHandle: Object,
277 + ): void {
278 + const viewConfig = instance.viewConfig;
279 +
280 + updateFiberProps(instance._nativeTag, newProps);
281 +
282 + const updatePayload = ReactNativeAttributePayload.diff(
283 + oldProps,
284 + newProps,
285 + viewConfig.validAttributes,
286 + );
287 +
288 + // Avoid the overhead of bridge calls if there's no update.
289 + // This is an expensive no-op for Android, and causes an unnecessary
290 + // view invalidation for certain components (eg RCTTextInput) on iOS.
291 + if (updatePayload != null) {
292 + UIManager.updateView(
293 + instance._nativeTag, // reactTag
294 + viewConfig.uiViewClassName, // viewName
295 + updatePayload, // props
296 + );
297 + }
298 + },
299 +
300 + insertBefore(
301 + parentInstance: Instance,
302 + child: Instance | TextInstance,
303 + beforeChild: Instance | TextInstance,
304 + ): void {
305 + const children = (parentInstance: any)._children;
306 + const index = children.indexOf(child);
307 +
308 + // Move existing child or add new child?
309 + if (index >= 0) {
310 + children.splice(index, 1);
311 + const beforeChildIndex = children.indexOf(beforeChild);
312 + children.splice(beforeChildIndex, 0, child);
313 +
314 + UIManager.manageChildren(
315 + (parentInstance: any)._nativeTag, // containerID
316 + [index], // moveFromIndices
317 + [beforeChildIndex], // moveToIndices
318 + [], // addChildReactTags
319 + [], // addAtIndices
320 + [], // removeAtIndices
321 + );
322 + } else {
323 + const beforeChildIndex = children.indexOf(beforeChild);
324 + children.splice(beforeChildIndex, 0, child);
325 +
326 + const childTag = typeof child === 'number' ? child : child._nativeTag;
327 +
328 + UIManager.manageChildren(
329 + (parentInstance: any)._nativeTag, // containerID
330 + [], // moveFromIndices
331 + [], // moveToIndices
332 + [childTag], // addChildReactTags
333 + [beforeChildIndex], // addAtIndices
334 + [], // removeAtIndices
335 + );
336 + }
337 + },
338 +
339 + insertInContainerBefore(
340 + parentInstance: Container,
341 + child: Instance | TextInstance,
342 + beforeChild: Instance | TextInstance,
343 + ): void {
344 + // TODO (bvaughn): Remove this check when...
345 + // We create a wrapper object for the container in ReactNative render()
346 + // Or we refactor to remove wrapper objects entirely.
347 + // For more info on pros/cons see PR #8560 description.
348 + invariant(
349 + typeof parentInstance !== 'number',
350 + 'Container does not support insertBefore operation',
351 + );
352 + },
353 +
354 + removeChild(
355 + parentInstance: Instance,
356 + child: Instance | TextInstance,
357 + ): void {
358 + recursivelyUncacheFiberNode(child);
359 + const children = parentInstance._children;
360 + const index = children.indexOf(child);
361 +
362 + children.splice(index, 1);
363 +
364 + UIManager.manageChildren(
365 + parentInstance._nativeTag, // containerID
366 + [], // moveFromIndices
367 + [], // moveToIndices
368 + [], // addChildReactTags
369 + [], // addAtIndices
370 + [index], // removeAtIndices
371 + );
372 + },
373 +
374 + removeChildFromContainer(
375 + parentInstance: Container,
376 + child: Instance | TextInstance,
377 + ): void {
378 + recursivelyUncacheFiberNode(child);
379 + UIManager.manageChildren(
380 + parentInstance, // containerID
381 + [], // moveFromIndices
382 + [], // moveToIndices
383 + [], // addChildReactTags
384 + [], // addAtIndices
385 + [0], // removeAtIndices
386 + );
387 + },
388 +
389 + resetTextContent(instance: Instance): void {
390 + // Noop
391 + },
392 + },
393 });
394
395 module.exports = NativeRenderer;
src/renderers/noop/ReactNoopEntry.js
+33 -31
@@ -137,28 +137,12 @@ var NoopRenderer = ReactFiberReconciler({
137 return UPDATE_SIGNAL;
138 },
139
140 - commitMount(instance: Instance, type: string, newProps: Props): void {
141 - // Noop
142 - },
143 -
144 - commitUpdate(
145 - instance: Instance,
146 - updatePayload: Object,
147 - type: string,
148 - oldProps: Props,
149 - newProps: Props,
150 - ): void {
151 - instance.prop = newProps.prop;
152 - },
153 -
140 shouldSetTextContent(type: string, props: Props): boolean {
141 return (
142 typeof props.children === 'string' || typeof props.children === 'number'
143 );
144 },
145
160 - resetTextContent(instance: Instance): void {},
161 -
146 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
147 return !!props.hidden;
148 },
@@ -175,21 +159,6 @@ var NoopRenderer = ReactFiberReconciler({
159 return inst;
160 },
161
178 - commitTextUpdate(
179 - textInstance: TextInstance,
180 - oldText: string,
181 - newText: string,
182 - ): void {
183 - textInstance.text = newText;
184 - },
185 -
186 - appendChild: appendChild,
187 - appendChildToContainer: appendChild,
188 - insertBefore: insertBefore,
189 - insertInContainerBefore: insertBefore,
190 - removeChild: removeChild,
191 - removeChildFromContainer: removeChild,
192 -
162 scheduleDeferredCallback(callback) {
163 if (scheduledCallback) {
164 throw new Error(
@@ -207,6 +176,39 @@ var NoopRenderer = ReactFiberReconciler({
176 now(): number {
177 return elapsedTimeInMs;
178 },
179 +
180 + mutation: {
181 + commitMount(instance: Instance, type: string, newProps: Props): void {
182 + // Noop
183 + },
184 +
185 + commitUpdate(
186 + instance: Instance,
187 + updatePayload: Object,
188 + type: string,
189 + oldProps: Props,
190 + newProps: Props,
191 + ): void {
192 + instance.prop = newProps.prop;
193 + },
194 +
195 + commitTextUpdate(
196 + textInstance: TextInstance,
197 + oldText: string,
198 + newText: string,
199 + ): void {
200 + textInstance.text = newText;
201 + },
202 +
203 + appendChild: appendChild,
204 + appendChildToContainer: appendChild,
205 + insertBefore: insertBefore,
206 + insertInContainerBefore: insertBefore,
207 + removeChild: removeChild,
208 + removeChildFromContainer: removeChild,
209 +
210 + resetTextContent(instance: Instance): void {},
211 + },
212 });
213
214 var rootContainers = new Map();
src/renderers/shared/fiber/ReactFiberCommitWork.js
+212 -187
@@ -47,19 +47,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
47 config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
48 captureError: (failedFiber: Fiber, error: mixed) => Fiber | null,
49 ) {
50 - const {
51 - commitMount,
52 - commitUpdate,
53 - resetTextContent,
54 - commitTextUpdate,
55 - appendChild,
56 - appendChildToContainer,
57 - insertBefore,
58 - insertInContainerBefore,
59 - removeChild,
60 - removeChildFromContainer,
61 - getPublicInstance,
62 - } = config;
50 + const {getPublicInstance} = config;
51
52 if (__DEV__) {
53 var callComponentWillUnmountWithTimerInDev = function(current, instance) {
@@ -115,6 +103,213 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
103 }
104 }
105
106 + function commitLifeCycles(current: Fiber | null, finishedWork: Fiber): void {
107 + switch (finishedWork.tag) {
108 + case ClassComponent: {
109 + const instance = finishedWork.stateNode;
110 + if (finishedWork.effectTag & Update) {
111 + if (current === null) {
112 + if (__DEV__) {
113 + startPhaseTimer(finishedWork, 'componentDidMount');
114 + }
115 + instance.props = finishedWork.memoizedProps;
116 + instance.state = finishedWork.memoizedState;
117 + instance.componentDidMount();
118 + if (__DEV__) {
119 + stopPhaseTimer();
120 + }
121 + } else {
122 + const prevProps = current.memoizedProps;
123 + const prevState = current.memoizedState;
124 + if (__DEV__) {
125 + startPhaseTimer(finishedWork, 'componentDidUpdate');
126 + }
127 + instance.props = finishedWork.memoizedProps;
128 + instance.state = finishedWork.memoizedState;
129 + instance.componentDidUpdate(prevProps, prevState);
130 + if (__DEV__) {
131 + stopPhaseTimer();
132 + }
133 + }
134 + }
135 + if (
136 + finishedWork.effectTag & Callback &&
137 + finishedWork.updateQueue !== null
138 + ) {
139 + commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
140 + }
141 + return;
142 + }
143 + case HostRoot: {
144 + const updateQueue = finishedWork.updateQueue;
145 + if (updateQueue !== null) {
146 + const instance = finishedWork.child && finishedWork.child.stateNode;
147 + commitCallbacks(finishedWork, updateQueue, instance);
148 + }
149 + return;
150 + }
151 + case HostComponent: {
152 + const instance: I = finishedWork.stateNode;
153 +
154 + // Renderers may schedule work to be done after host components are mounted
155 + // (eg DOM renderer may schedule auto-focus for inputs and form controls).
156 + // These effects should only be committed when components are first mounted,
157 + // aka when there is no current/alternate.
158 + if (current === null && finishedWork.effectTag & Update) {
159 + const type = finishedWork.type;
160 + const props = finishedWork.memoizedProps;
161 + commitMount(instance, type, props, finishedWork);
162 + }
163 +
164 + return;
165 + }
166 + case HostText: {
167 + // We have no life-cycles associated with text.
168 + return;
169 + }
170 + case HostPortal: {
171 + // We have no life-cycles associated with portals.
172 + return;
173 + }
174 + default: {
175 + invariant(
176 + false,
177 + 'This unit of work tag should not have side-effects. This error is ' +
178 + 'likely caused by a bug in React. Please file an issue.',
179 + );
180 + }
181 + }
182 + }
183 +
184 + function commitAttachRef(finishedWork: Fiber) {
185 + const ref = finishedWork.ref;
186 + if (ref !== null) {
187 + const instance = finishedWork.stateNode;
188 + switch (finishedWork.tag) {
189 + case HostComponent:
190 + ref(getPublicInstance(instance));
191 + break;
192 + default:
193 + ref(instance);
194 + }
195 + }
196 + }
197 +
198 + function commitDetachRef(current: Fiber) {
199 + const currentRef = current.ref;
200 + if (currentRef !== null) {
201 + currentRef(null);
202 + }
203 + }
204 +
205 + // User-originating errors (lifecycles and refs) should not interrupt
206 + // deletion, so don't let them throw. Host-originating errors should
207 + // interrupt deletion, so it's okay
208 + function commitUnmount(current: Fiber): void {
209 + if (typeof onCommitUnmount === 'function') {
210 + onCommitUnmount(current);
211 + }
212 +
213 + switch (current.tag) {
214 + case ClassComponent: {
215 + safelyDetachRef(current);
216 + const instance = current.stateNode;
217 + if (typeof instance.componentWillUnmount === 'function') {
218 + safelyCallComponentWillUnmount(current, instance);
219 + }
220 + return;
221 + }
222 + case HostComponent: {
223 + safelyDetachRef(current);
224 + return;
225 + }
226 + case CoroutineComponent: {
227 + commitNestedUnmounts(current.stateNode);
228 + return;
229 + }
230 + case HostPortal: {
231 + // TODO: this is recursive.
232 + // We are also not using this parent because
233 + // the portal will get pushed immediately.
234 + unmountHostComponents(current);
235 + return;
236 + }
237 + }
238 + }
239 +
240 + function commitNestedUnmounts(root: Fiber): void {
241 + // While we're inside a removed host node we don't want to call
242 + // removeChild on the inner nodes because they're removed by the top
243 + // call anyway. We also want to call componentWillUnmount on all
244 + // composites before this host node is removed from the tree. Therefore
245 + // we do an inner loop while we're still inside the host node.
246 + let node: Fiber = root;
247 + while (true) {
248 + commitUnmount(node);
249 + // Visit children because they may contain more composite or host nodes.
250 + // Skip portals because commitUnmount() currently visits them recursively.
251 + if (node.child !== null && node.tag !== HostPortal) {
252 + node.child.return = node;
253 + node = node.child;
254 + continue;
255 + }
256 + if (node === root) {
257 + return;
258 + }
259 + while (node.sibling === null) {
260 + if (node.return === null || node.return === root) {
261 + return;
262 + }
263 + node = node.return;
264 + }
265 + node.sibling.return = node.return;
266 + node = node.sibling;
267 + }
268 + }
269 +
270 + function detachFiber(current: Fiber) {
271 + // Cut off the return pointers to disconnect it from the tree. Ideally, we
272 + // should clear the child pointer of the parent alternate to let this
273 + // get GC:ed but we don't know which for sure which parent is the current
274 + // one so we'll settle for GC:ing the subtree of this child. This child
275 + // itself will be GC:ed when the parent updates the next time.
276 + current.return = null;
277 + current.child = null;
278 + if (current.alternate) {
279 + current.alternate.child = null;
280 + current.alternate.return = null;
281 + }
282 + }
283 +
284 + if (!config.mutation) {
285 + return {
286 + commitResetTextContent(finishedWork: Fiber) {},
287 + commitPlacement(finishedWork: Fiber) {},
288 + commitDeletion(current: Fiber) {
289 + // Detach refs and call componentWillUnmount() on the whole subtree.
290 + commitNestedUnmounts(current);
291 + detachFiber(current);
292 + },
293 + commitWork(current: Fiber | null, finishedWork: Fiber) {},
294 + commitLifeCycles,
295 + commitAttachRef,
296 + commitDetachRef,
297 + };
298 + }
299 +
300 + const {
301 + commitMount,
302 + commitUpdate,
303 + resetTextContent,
304 + commitTextUpdate,
305 + appendChild,
306 + appendChildToContainer,
307 + insertBefore,
308 + insertInContainerBefore,
309 + removeChild,
310 + removeChildFromContainer,
311 + } = config.mutation;
312 +
313 function getHostParentFiber(fiber: Fiber): Fiber {
314 let parent = fiber.return;
315 while (parent !== null) {
@@ -254,36 +449,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
449 }
450 }
451
257 - function commitNestedUnmounts(root: Fiber): void {
258 - // While we're inside a removed host node we don't want to call
259 - // removeChild on the inner nodes because they're removed by the top
260 - // call anyway. We also want to call componentWillUnmount on all
261 - // composites before this host node is removed from the tree. Therefore
262 - // we do an inner loop while we're still inside the host node.
263 - let node: Fiber = root;
264 - while (true) {
265 - commitUnmount(node);
266 - // Visit children because they may contain more composite or host nodes.
267 - // Skip portals because commitUnmount() currently visits them recursively.
268 - if (node.child !== null && node.tag !== HostPortal) {
269 - node.child.return = node;
270 - node = node.child;
271 - continue;
272 - }
273 - if (node === root) {
274 - return;
275 - }
276 - while (node.sibling === null) {
277 - if (node.return === null || node.return === root) {
278 - return;
279 - }
280 - node = node.return;
281 - }
282 - node.sibling.return = node.return;
283 - node = node.sibling;
284 - }
285 - }
286 -
452 function unmountHostComponents(current): void {
453 // We only have the top Fiber that was inserted but we need recurse down its
454 // children to find all the terminal nodes.
@@ -375,53 +540,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
540 // Recursively delete all host nodes from the parent.
541 // Detach refs and call componentWillUnmount() on the whole subtree.
542 unmountHostComponents(current);
378 -
379 - // Cut off the return pointers to disconnect it from the tree. Ideally, we
380 - // should clear the child pointer of the parent alternate to let this
381 - // get GC:ed but we don't know which for sure which parent is the current
382 - // one so we'll settle for GC:ing the subtree of this child. This child
383 - // itself will be GC:ed when the parent updates the next time.
384 - current.return = null;
385 - current.child = null;
386 - if (current.alternate) {
387 - current.alternate.child = null;
388 - current.alternate.return = null;
389 - }
390 - }
391 -
392 - // User-originating errors (lifecycles and refs) should not interrupt
393 - // deletion, so don't let them throw. Host-originating errors should
394 - // interrupt deletion, so it's okay
395 - function commitUnmount(current: Fiber): void {
396 - if (typeof onCommitUnmount === 'function') {
397 - onCommitUnmount(current);
398 - }
399 -
400 - switch (current.tag) {
401 - case ClassComponent: {
402 - safelyDetachRef(current);
403 - const instance = current.stateNode;
404 - if (typeof instance.componentWillUnmount === 'function') {
405 - safelyCallComponentWillUnmount(current, instance);
406 - }
407 - return;
408 - }
409 - case HostComponent: {
410 - safelyDetachRef(current);
411 - return;
412 - }
413 - case CoroutineComponent: {
414 - commitNestedUnmounts(current.stateNode);
415 - return;
416 - }
417 - case HostPortal: {
418 - // TODO: this is recursive.
419 - // We are also not using this parent because
420 - // the portal will get pushed immediately.
421 - unmountHostComponents(current);
422 - return;
423 - }
424 - }
543 + detachFiber(current);
544 }
545
546 function commitWork(current: Fiber | null, finishedWork: Fiber): void {
@@ -488,106 +607,12 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
607 }
608 }
609
491 - function commitLifeCycles(current: Fiber | null, finishedWork: Fiber): void {
492 - switch (finishedWork.tag) {
493 - case ClassComponent: {
494 - const instance = finishedWork.stateNode;
495 - if (finishedWork.effectTag & Update) {
496 - if (current === null) {
497 - if (__DEV__) {
498 - startPhaseTimer(finishedWork, 'componentDidMount');
499 - }
500 - instance.props = finishedWork.memoizedProps;
501 - instance.state = finishedWork.memoizedState;
502 - instance.componentDidMount();
503 - if (__DEV__) {
504 - stopPhaseTimer();
505 - }
506 - } else {
507 - const prevProps = current.memoizedProps;
508 - const prevState = current.memoizedState;
509 - if (__DEV__) {
510 - startPhaseTimer(finishedWork, 'componentDidUpdate');
511 - }
512 - instance.props = finishedWork.memoizedProps;
513 - instance.state = finishedWork.memoizedState;
514 - instance.componentDidUpdate(prevProps, prevState);
515 - if (__DEV__) {
516 - stopPhaseTimer();
517 - }
518 - }
519 - }
520 - if (
521 - finishedWork.effectTag & Callback &&
522 - finishedWork.updateQueue !== null
523 - ) {
524 - commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
525 - }
526 - return;
527 - }
528 - case HostRoot: {
529 - const updateQueue = finishedWork.updateQueue;
530 - if (updateQueue !== null) {
531 - const instance = finishedWork.child && finishedWork.child.stateNode;
532 - commitCallbacks(finishedWork, updateQueue, instance);
533 - }
534 - return;
535 - }
536 - case HostComponent: {
537 - const instance: I = finishedWork.stateNode;
538 -
539 - // Renderers may schedule work to be done after host components are mounted
540 - // (eg DOM renderer may schedule auto-focus for inputs and form controls).
541 - // These effects should only be committed when components are first mounted,
542 - // aka when there is no current/alternate.
543 - if (current === null && finishedWork.effectTag & Update) {
544 - const type = finishedWork.type;
545 - const props = finishedWork.memoizedProps;
546 - commitMount(instance, type, props, finishedWork);
547 - }
548 -
549 - return;
550 - }
551 - case HostText: {
552 - // We have no life-cycles associated with text.
553 - return;
554 - }
555 - case HostPortal: {
556 - // We have no life-cycles associated with portals.
557 - return;
558 - }
559 - default: {
560 - invariant(
561 - false,
562 - 'This unit of work tag should not have side-effects. This error is ' +
563 - 'likely caused by a bug in React. Please file an issue.',
564 - );
565 - }
566 - }
567 - }
568 -
569 - function commitAttachRef(finishedWork: Fiber) {
570 - const ref = finishedWork.ref;
571 - if (ref !== null) {
572 - const instance = finishedWork.stateNode;
573 - switch (finishedWork.tag) {
574 - case HostComponent:
575 - ref(getPublicInstance(instance));
576 - break;
577 - default:
578 - ref(instance);
579 - }
580 - }
581 - }
582 -
583 - function commitDetachRef(current: Fiber) {
584 - const currentRef = current.ref;
585 - if (currentRef !== null) {
586 - currentRef(null);
587 - }
610 + function commitResetTextContent(current: Fiber) {
611 + resetTextContent(current.stateNode);
612 }
613
614 return {
615 + commitResetTextContent,
616 commitPlacement,
617 commitDeletion,
618 commitWork,
src/renderers/shared/fiber/ReactFiberHydrationContext.js
+20 -34
@@ -36,42 +36,10 @@ export type HydrationContext<C, CX> = {
36 module.exports = function<T, P, I, TI, PI, C, CX, PL>(
37 config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
38 ): HydrationContext<C, CX> {
39 - const {
40 - shouldSetTextContent,
41 - canHydrateInstance,
42 - canHydrateTextInstance,
43 - getNextHydratableSibling,
44 - getFirstHydratableChild,
45 - hydrateInstance,
46 - hydrateTextInstance,
47 - didNotMatchHydratedContainerTextInstance,
48 - didNotMatchHydratedTextInstance,
49 - didNotHydrateContainerInstance,
50 - didNotHydrateInstance,
51 - // TODO: These are currently unused, see below.
52 - // didNotFindHydratableContainerInstance,
53 - // didNotFindHydratableContainerTextInstance,
54 - didNotFindHydratableInstance,
55 - didNotFindHydratableTextInstance,
56 - } = config;
39 + const {shouldSetTextContent, hydration} = config;
40
41 // If this doesn't have hydration mode.
59 - if (
60 - !(canHydrateInstance &&
61 - canHydrateTextInstance &&
62 - getNextHydratableSibling &&
63 - getFirstHydratableChild &&
64 - hydrateInstance &&
65 - hydrateTextInstance &&
66 - didNotMatchHydratedContainerTextInstance &&
67 - didNotMatchHydratedTextInstance &&
68 - didNotHydrateContainerInstance &&
69 - didNotHydrateInstance &&
70 - // didNotFindHydratableContainerInstance &&
71 - // didNotFindHydratableContainerTextInstance &&
72 - didNotFindHydratableInstance &&
73 - didNotFindHydratableTextInstance)
74 - ) {
42 + if (!hydration) {
43 return {
44 enterHydrationState() {
45 return false;
@@ -98,6 +66,24 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
66 };
67 }
68
69 + const {
70 + canHydrateInstance,
71 + canHydrateTextInstance,
72 + getNextHydratableSibling,
73 + getFirstHydratableChild,
74 + hydrateInstance,
75 + hydrateTextInstance,
76 + didNotMatchHydratedContainerTextInstance,
77 + didNotMatchHydratedTextInstance,
78 + didNotHydrateContainerInstance,
79 + didNotHydrateInstance,
80 + // TODO: These are currently unused, see below.
81 + // didNotFindHydratableContainerInstance,
82 + // didNotFindHydratableContainerTextInstance,
83 + didNotFindHydratableInstance,
84 + didNotFindHydratableTextInstance,
85 + } = hydration;
86 +
87 // The deepest Fiber on the stack involved in a hydration context.
88 // This may have been an insertion or a hydration.
89 let hydrationParentFiber: null | Fiber = null;
src/renderers/shared/fiber/ReactFiberReconciler.js
+77 -47
@@ -77,6 +77,35 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
77 rootContainerInstance: C,
78 hostContext: CX,
79 ): null | PL,
80 +
81 + shouldSetTextContent(type: T, props: P): boolean,
82 + shouldDeprioritizeSubtree(type: T, props: P): boolean,
83 +
84 + createTextInstance(
85 + text: string,
86 + rootContainerInstance: C,
87 + hostContext: CX,
88 + internalInstanceHandle: OpaqueHandle,
89 + ): TI,
90 +
91 + scheduleDeferredCallback(
92 + callback: (deadline: Deadline) => void,
93 + ): number | void,
94 +
95 + prepareForCommit(): void,
96 + resetAfterCommit(): void,
97 +
98 + now(): number,
99 +
100 + useSyncScheduling?: boolean,
101 +
102 + +hydration?: HydrationHostConfig<T, P, I, TI, C, CX, PL>,
103 +
104 + +mutation?: MutableUpdatesHostConfig<T, P, I, TI, C, PL>,
105 + +persistence?: PersistentUpdatesHostConfig<T, P, I, C, CX, PL>,
106 +};
107 +
108 +type MutableUpdatesHostConfig<T, P, I, TI, C, PL> = {
109 commitUpdate(
110 instance: I,
111 updatePayload: PL,
@@ -91,19 +120,8 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
120 newProps: P,
121 internalInstanceHandle: OpaqueHandle,
122 ): void,
94 -
95 - shouldSetTextContent(type: T, props: P): boolean,
96 - resetTextContent(instance: I): void,
97 - shouldDeprioritizeSubtree(type: T, props: P): boolean,
98 -
99 - createTextInstance(
100 - text: string,
101 - rootContainerInstance: C,
102 - hostContext: CX,
103 - internalInstanceHandle: OpaqueHandle,
104 - ): TI,
123 commitTextUpdate(textInstance: TI, oldText: string, newText: string): void,
106 -
124 + resetTextContent(instance: I): void,
125 appendChild(parentInstance: I, child: I | TI): void,
126 appendChildToContainer(container: C, child: I | TI): void,
127 insertBefore(parentInstance: I, child: I | TI, beforeChild: I | TI): void,
@@ -114,80 +132,92 @@ export type HostConfig<T, P, I, TI, PI, C, CX, PL> = {
132 ): void,
133 removeChild(parentInstance: I, child: I | TI): void,
134 removeChildFromContainer(container: C, child: I | TI): void,
135 +};
136
118 - scheduleDeferredCallback(
119 - callback: (deadline: Deadline) => void,
120 - ): number | void,
121 -
122 - prepareForCommit(): void,
123 - resetAfterCommit(): void,
137 +type PersistentUpdatesHostConfig<T, P, I, C, CX, PL> = {
138 + cloneInstance(
139 + instance: I,
140 + updatePayload: PL,
141 + type: T,
142 + oldProps: P,
143 + newProps: P,
144 + internalInstanceHandle: OpaqueHandle,
145 + keepChildren: boolean,
146 + ): I,
147 + tryToReuseInstance(
148 + instance: I,
149 + updatePayload: PL,
150 + type: T,
151 + oldProps: P,
152 + newProps: P,
153 + internalInstanceHandle: OpaqueHandle,
154 + keepChildren: boolean,
155 + ): I,
156
125 - now(): number,
157 + createRootInstance(rootContainerInstance: C, hostContext: CX): I,
158 + commitRootInstance(rootInstance: I): void,
159 +};
160
161 +type HydrationHostConfig<T, P, I, TI, C, CX, PL> = {
162 // Optional hydration
128 - canHydrateInstance?: (instance: I | TI, type: T, props: P) => boolean,
129 - canHydrateTextInstance?: (instance: I | TI, text: string) => boolean,
130 - getNextHydratableSibling?: (instance: I | TI) => null | I | TI,
131 - getFirstHydratableChild?: (parentInstance: I | C) => null | I | TI,
132 - hydrateInstance?: (
163 + canHydrateInstance(instance: I | TI, type: T, props: P): boolean,
164 + canHydrateTextInstance(instance: I | TI, text: string): boolean,
165 + getNextHydratableSibling(instance: I | TI): null | I | TI,
166 + getFirstHydratableChild(parentInstance: I | C): null | I | TI,
167 + hydrateInstance(
168 instance: I,
169 type: T,
170 props: P,
171 rootContainerInstance: C,
172 hostContext: CX,
173 internalInstanceHandle: OpaqueHandle,
139 - ) => null | PL,
140 - hydrateTextInstance?: (
174 + ): null | PL,
175 + hydrateTextInstance(
176 textInstance: TI,
177 text: string,
178 internalInstanceHandle: OpaqueHandle,
144 - ) => boolean,
145 - didNotMatchHydratedContainerTextInstance?: (
179 + ): boolean,
180 + didNotMatchHydratedContainerTextInstance(
181 parentContainer: C,
182 textInstance: TI,
183 text: string,
149 - ) => void,
150 - didNotMatchHydratedTextInstance?: (
184 + ): void,
185 + didNotMatchHydratedTextInstance(
186 parentType: T,
187 parentProps: P,
188 parentInstance: I,
189 textInstance: TI,
190 text: string,
156 - ) => void,
157 - didNotHydrateContainerInstance?: (
158 - parentContainer: C,
159 - instance: I | TI,
160 - ) => void,
161 - didNotHydrateInstance?: (
191 + ): void,
192 + didNotHydrateContainerInstance(parentContainer: C, instance: I | TI): void,
193 + didNotHydrateInstance(
194 parentType: T,
195 parentProps: P,
196 parentInstance: I,
197 instance: I | TI,
166 - ) => void,
167 - didNotFindHydratableContainerInstance?: (
198 + ): void,
199 + didNotFindHydratableContainerInstance(
200 parentContainer: C,
201 type: T,
202 props: P,
171 - ) => void,
172 - didNotFindHydratableContainerTextInstance?: (
203 + ): void,
204 + didNotFindHydratableContainerTextInstance(
205 parentContainer: C,
206 text: string,
175 - ) => void,
176 - didNotFindHydratableInstance?: (
207 + ): void,
208 + didNotFindHydratableInstance(
209 parentType: T,
210 parentProps: P,
211 parentInstance: I,
212 type: T,
213 props: P,
182 - ) => void,
183 - didNotFindHydratableTextInstance?: (
214 + ): void,
215 + didNotFindHydratableTextInstance(
216 parentType: T,
217 parentProps: P,
218 parentInstance: I,
219 text: string,
188 - ) => void,
189 -
190 - useSyncScheduling?: boolean,
220 + ): void,
221 };
222
223 export type Reconciler<C, I, TI> = {
src/renderers/shared/fiber/ReactFiberScheduler.js
+2 -1
@@ -174,6 +174,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
174 hydrationContext,
175 );
176 const {
177 + commitResetTextContent,
178 commitPlacement,
179 commitDeletion,
180 commitWork,
@@ -332,7 +333,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
333
334 const effectTag = nextEffect.effectTag;
335 if (effectTag & ContentReset) {
335 - config.resetTextContent(nextEffect.stateNode);
336 + commitResetTextContent(nextEffect);
337 }
338
339 if (effectTag & Ref) {
src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
+5 -3
@@ -43,13 +43,15 @@ describe('ReactFiberHostContext', () => {
43 appendInitialChild: function() {
44 return null;
45 },
46 - appendChildToContainer: function() {
47 - return null;
48 - },
46 now: function() {
47 return 0;
48 },
49 useSyncScheduling: true,
50 + mutation: {
51 + appendChildToContainer: function() {
52 + return null;
53 + },
54 + },
55 });
56
57 const container = Renderer.createContainer(/* root: */ null);
src/renderers/testing/ReactTestRendererFiberEntry.js
+41 -40
@@ -180,35 +180,10 @@ var TestRenderer = ReactFiberReconciler({
180 return UPDATE_SIGNAL;
181 },
182
183 - commitUpdate(
184 - instance: Instance,
185 - updatePayload: {},
186 - type: string,
187 - oldProps: Props,
188 - newProps: Props,
189 - internalInstanceHandle: Object,
190 - ): void {
191 - instance.type = type;
192 - instance.props = newProps;
193 - },
194 -
195 - commitMount(
196 - instance: Instance,
197 - type: string,
198 - newProps: Props,
199 - internalInstanceHandle: Object,
200 - ): void {
201 - // noop
202 - },
203 -
183 shouldSetTextContent(type: string, props: Props): boolean {
184 return false;
185 },
186
208 - resetTextContent(testElement: Instance): void {
209 - // noop
210 - },
211 -
187 shouldDeprioritizeSubtree(type: string, props: Props): boolean {
188 return false;
189 },
@@ -225,21 +200,6 @@ var TestRenderer = ReactFiberReconciler({
200 };
201 },
202
228 - commitTextUpdate(
229 - textInstance: TextInstance,
230 - oldText: string,
231 - newText: string,
232 - ): void {
233 - textInstance.text = newText;
234 - },
235 -
236 - appendChild: appendChild,
237 - appendChildToContainer: appendChild,
238 - insertBefore: insertBefore,
239 - insertInContainerBefore: insertBefore,
240 - removeChild: removeChild,
241 - removeChildFromContainer: removeChild,
242 -
203 scheduleDeferredCallback(fn: Function): void {
204 setTimeout(fn, 0, {timeRemaining: Infinity});
205 },
@@ -252,6 +212,47 @@ var TestRenderer = ReactFiberReconciler({
212 // Test renderer does not use expiration
213 return 0;
214 },
215 +
216 + mutation: {
217 + commitUpdate(
218 + instance: Instance,
219 + updatePayload: {},
220 + type: string,
221 + oldProps: Props,
222 + newProps: Props,
223 + internalInstanceHandle: Object,
224 + ): void {
225 + instance.type = type;
226 + instance.props = newProps;
227 + },
228 +
229 + commitMount(
230 + instance: Instance,
231 + type: string,
232 + newProps: Props,
233 + internalInstanceHandle: Object,
234 + ): void {
235 + // noop
236 + },
237 +
238 + commitTextUpdate(
239 + textInstance: TextInstance,
240 + oldText: string,
241 + newText: string,
242 + ): void {
243 + textInstance.text = newText;
244 + },
245 + resetTextContent(testElement: Instance): void {
246 + // noop
247 + },
248 +
249 + appendChild: appendChild,
250 + appendChildToContainer: appendChild,
251 + insertBefore: insertBefore,
252 + insertInContainerBefore: insertBefore,
253 + removeChild: removeChild,
254 + removeChildFromContainer: removeChild,
255 + },
256 });
257
258 var defaultTestOptions = {