diff --git a/fixtures/reconciler/index.js b/fixtures/reconciler/index.js
index b178006992..8c99bb7541 100644
--- a/fixtures/reconciler/index.js
+++ b/fixtures/reconciler/index.js
@@ -115,28 +115,12 @@ var NoopRenderer = ReactFiberReconciler({
return UPDATE_SIGNAL;
},
- commitMount(instance: Instance, type: string, newProps: Props): void {
- // Noop
- },
-
- commitUpdate(
- instance: Instance,
- updatePayload: Object,
- type: string,
- oldProps: Props,
- newProps: Props
- ): void {
- instance.prop = newProps.prop;
- },
-
shouldSetTextContent(type: string, props: Props): boolean {
return (
typeof props.children === 'string' || typeof props.children === 'number'
);
},
- resetTextContent(instance: Instance): void {},
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return !!props.hidden;
},
@@ -155,21 +139,6 @@ var NoopRenderer = ReactFiberReconciler({
return inst;
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string
- ): void {
- textInstance.text = newText;
- },
-
- appendChild: appendChild,
- appendChildToContainer: appendChild,
- insertBefore: insertBefore,
- insertInContainerBefore: insertBefore,
- removeChild: removeChild,
- removeChildFromContainer: removeChild,
-
scheduleDeferredCallback(callback) {
if (scheduledCallback) {
throw new Error(
@@ -183,6 +152,39 @@ var NoopRenderer = ReactFiberReconciler({
prepareForCommit(): void {},
resetAfterCommit(): void {},
+
+ mutation: {
+ commitMount(instance: Instance, type: string, newProps: Props): void {
+ // Noop
+ },
+
+ commitUpdate(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props
+ ): void {
+ instance.prop = newProps.prop;
+ },
+
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string
+ ): void {
+ textInstance.text = newText;
+ },
+
+ appendChild: appendChild,
+ appendChildToContainer: appendChild,
+ insertBefore: insertBefore,
+ insertInContainerBefore: insertBefore,
+ removeChild: removeChild,
+ removeChildFromContainer: removeChild,
+
+ resetTextContent(instance: Instance): void {},
+ },
});
var rootContainers = new Map();
diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js
index eb9cbe346d..d2e325c20f 100644
--- a/scripts/rollup/build.js
+++ b/scripts/rollup/build.js
@@ -21,6 +21,7 @@ const Stats = require('./stats');
const syncReactDom = require('./sync').syncReactDom;
const syncReactNative = require('./sync').syncReactNative;
const syncReactNativeRT = require('./sync').syncReactNativeRT;
+const syncReactNativeCS = require('./sync').syncReactNativeCS;
const Packaging = require('./packaging');
const Header = require('./header');
const closure = require('rollup-plugin-closure-compiler-js');
@@ -571,6 +572,7 @@ rimraf('build', () => {
Packaging.createFacebookWWWBuild,
Packaging.createReactNativeBuild,
Packaging.createReactNativeRTBuild,
+ Packaging.createReactNativeCSBuild,
];
for (const bundle of Bundles.bundles) {
tasks.push(
@@ -591,6 +593,9 @@ rimraf('build', () => {
tasks.push(() =>
syncReactNativeRT(join('build', 'react-native-rt'), syncFbsource)
);
+ tasks.push(() =>
+ syncReactNativeCS(join('build', 'react-native-cs'), syncFbsource)
+ );
} else if (syncWww) {
tasks.push(() => syncReactDom(join('build', 'facebook-www'), syncWww));
}
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index 6ba9f3dd4a..60dc4994d7 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -298,7 +298,7 @@ const bundles = [
useFiber: true,
},
- /******* React Native *******/
+ /******* React Native RT *******/
{
babelOpts: babelOptsReact,
bundleTypes: [RN_DEV, RN_PROD],
@@ -332,6 +332,32 @@ const bundles = [
useFiber: true,
},
+ /******* React Native CS *******/
+ {
+ babelOpts: babelOptsReact,
+ bundleTypes: [RN_DEV, RN_PROD],
+ config: {
+ destDir: 'build/',
+ moduleName: 'ReactNativeCSFiber',
+ sourceMap: false,
+ },
+ entry: 'src/renderers/native-cs/ReactNativeCSFiberEntry',
+ externals: ['prop-types/checkPropTypes'],
+ hasteName: 'ReactNativeCSFiber',
+ isRenderer: true,
+ label: 'native-cs-fiber',
+ manglePropertiesOnProd: false,
+ name: 'react-native-cs-renderer',
+ paths: [
+ 'src/renderers/native-cs/**/*.js',
+ 'src/renderers/shared/**/*.js',
+
+ 'src/ReactVersion.js',
+ 'src/shared/**/*.js',
+ ],
+ useFiber: true,
+ },
+
/******* React Test Renderer *******/
{
babelOpts: babelOptsReact,
diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js
index 33ce443ed0..23d75f9a12 100644
--- a/scripts/rollup/packaging.js
+++ b/scripts/rollup/packaging.js
@@ -33,6 +33,11 @@ const reactNativeRTSrcDependencies = [
'src/renderers/native-rt/ReactNativeRTTypes.js',
];
+// these files need to be copied to the react-native-cs build
+const reactNativeCSSrcDependencies = [
+ 'src/renderers/native-cs/ReactNativeCSTypes.js',
+];
+
function getPackageName(name) {
if (name.indexOf('/') !== -1) {
return name.split('/')[0];
@@ -81,6 +86,25 @@ function createReactNativeRTBuild() {
return Promise.all(promises);
}
+function createReactNativeCSBuild() {
+ // create the react-native-cs folder for FB bundles
+ fs.mkdirSync(join('build', 'react-native-cs'));
+ // create the react-native-cs shims folder for FB shims
+ fs.mkdirSync(join('build', 'react-native-cs', 'shims'));
+
+ const to = join('build', 'react-native-cs', 'shims');
+
+ let promises = [];
+ // we also need to copy over some specific files from src
+ // defined in reactNativeCSSrcDependencies
+ for (const srcDependency of reactNativeCSSrcDependencies) {
+ promises.push(
+ asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency)))
+ );
+ }
+ return Promise.all(promises);
+}
+
function createFacebookWWWBuild() {
// create the facebookWWW folder for FB bundles
fs.mkdirSync(join('build', facebookWWW));
@@ -191,4 +215,5 @@ module.exports = {
createFacebookWWWBuild,
createReactNativeBuild,
createReactNativeRTBuild,
+ createReactNativeCSBuild,
};
diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json
index 1e345fb8c7..b550705909 100644
--- a/scripts/rollup/results.json
+++ b/scripts/rollup/results.json
@@ -25,28 +25,28 @@
"gzip": 6703
},
"react-dom.development.js (UMD_DEV)": {
- "size": 630418,
- "gzip": 145239
+ "size": 625837,
+ "gzip": 144320
},
"react-dom.production.min.js (UMD_PROD)": {
- "size": 101638,
- "gzip": 32128
+ "size": 100866,
+ "gzip": 31848
},
"react-dom.development.js (NODE_DEV)": {
- "size": 592683,
- "gzip": 136496
+ "size": 588106,
+ "gzip": 135609
},
"react-dom.production.min.js (NODE_PROD)": {
- "size": 106507,
- "gzip": 33538
+ "size": 105343,
+ "gzip": 33129
},
"ReactDOMFiber-dev.js (FB_DEV)": {
- "size": 589582,
- "gzip": 135915
+ "size": 584995,
+ "gzip": 135013
},
"ReactDOMFiber-prod.js (FB_PROD)": {
- "size": 419983,
- "gzip": 93676
+ "size": 414881,
+ "gzip": 92663
},
"react-dom-test-utils.development.js (NODE_DEV)": {
"size": 41660,
@@ -113,44 +113,44 @@
"gzip": 6214
},
"react-art.development.js (UMD_DEV)": {
- "size": 376896,
- "gzip": 83318
+ "size": 372576,
+ "gzip": 82452
},
"react-art.production.min.js (UMD_PROD)": {
- "size": 83700,
- "gzip": 25995
+ "size": 83057,
+ "gzip": 25828
},
"react-art.development.js (NODE_DEV)": {
- "size": 301201,
- "gzip": 64095
+ "size": 296909,
+ "gzip": 63228
},
"react-art.production.min.js (NODE_PROD)": {
- "size": 53531,
- "gzip": 16898
+ "size": 52508,
+ "gzip": 16427
},
"ReactARTFiber-dev.js (FB_DEV)": {
- "size": 300073,
- "gzip": 64131
+ "size": 295771,
+ "gzip": 63258
},
"ReactARTFiber-prod.js (FB_PROD)": {
- "size": 224585,
- "gzip": 46566
+ "size": 219800,
+ "gzip": 45628
},
"ReactNativeFiber-dev.js (RN_DEV)": {
- "size": 283095,
- "gzip": 49176
+ "size": 279764,
+ "gzip": 48665
},
"ReactNativeFiber-prod.js (RN_PROD)": {
- "size": 221913,
- "gzip": 38509
+ "size": 218506,
+ "gzip": 37873
},
"react-test-renderer.development.js (NODE_DEV)": {
- "size": 304971,
- "gzip": 64484
+ "size": 300619,
+ "gzip": 63624
},
"ReactTestRendererFiber-dev.js (FB_DEV)": {
- "size": 303833,
- "gzip": 64527
+ "size": 299471,
+ "gzip": 63657
},
"react-test-renderer-shallow.development.js (NODE_DEV)": {
"size": 9215,
@@ -161,8 +161,8 @@
"gzip": 2221
},
"react-noop-renderer.development.js (NODE_DEV)": {
- "size": 292571,
- "gzip": 61440
+ "size": 288214,
+ "gzip": 60549
},
"react-dom-server.development.js (UMD_DEV)": {
"size": 120897,
@@ -189,16 +189,16 @@
"gzip": 7520
},
"ReactNativeRTFiber-dev.js (RN_DEV)": {
- "size": 215098,
- "gzip": 36673
+ "size": 211687,
+ "gzip": 36145
},
"ReactNativeRTFiber-prod.js (RN_PROD)": {
- "size": 163687,
- "gzip": 27527
+ "size": 160200,
+ "gzip": 26880
},
"react-test-renderer.production.min.js (NODE_PROD)": {
- "size": 55085,
- "gzip": 17135
+ "size": 54076,
+ "gzip": 16776
},
"react-test-renderer-shallow.production.min.js (NODE_PROD)": {
"size": 4536,
@@ -209,12 +209,20 @@
"gzip": 4241
},
"react-reconciler.development.js (NODE_DEV)": {
- "size": 279927,
- "gzip": 58576
+ "size": 275518,
+ "gzip": 57698
},
"react-reconciler.production.min.js (NODE_PROD)": {
- "size": 38630,
- "gzip": 12165
+ "size": 37979,
+ "gzip": 11842
+ },
+ "ReactNativeCSFiber-dev.js (RN_DEV)": {
+ "size": 204077,
+ "gzip": 34318
+ },
+ "ReactNativeCSFiber-prod.js (RN_PROD)": {
+ "size": 155097,
+ "gzip": 25642
}
}
}
\ No newline at end of file
diff --git a/src/renderers/art/ReactARTFiberEntry.js b/src/renderers/art/ReactARTFiberEntry.js
index ca97c71e8d..7189b0b871 100644
--- a/src/renderers/art/ReactARTFiberEntry.js
+++ b/src/renderers/art/ReactARTFiberEntry.js
@@ -388,20 +388,6 @@ class Text extends React.Component {
/** ART Renderer */
const ARTRenderer = ReactFiberReconciler({
- appendChild(parentInstance, child) {
- if (child.parentNode === parentInstance) {
- child.eject();
- }
- child.inject(parentInstance);
- },
-
- appendChildToContainer(parentInstance, child) {
- if (child.parentNode === parentInstance) {
- child.eject();
- }
- child.inject(parentInstance);
- },
-
appendInitialChild(parentInstance, child) {
if (typeof child === 'string') {
// Noop for string children of Text (eg {'foo'}{'bar'})
@@ -412,18 +398,6 @@ const ARTRenderer = ReactFiberReconciler({
child.inject(parentInstance);
},
- commitTextUpdate(textInstance, oldText, newText) {
- // Noop
- },
-
- commitMount(instance, type, newProps) {
- // Noop
- },
-
- commitUpdate(instance, updatePayload, type, oldProps, newProps) {
- instance._applyProps(instance, newProps, oldProps);
- },
-
createInstance(type, props, internalInstanceHandle) {
let instance;
@@ -470,22 +444,6 @@ const ARTRenderer = ReactFiberReconciler({
return instance;
},
- insertBefore(parentInstance, child, beforeChild) {
- invariant(
- child !== beforeChild,
- 'ReactART: Can not insert node before itself',
- );
- child.injectBefore(beforeChild);
- },
-
- insertInContainerBefore(parentInstance, child, beforeChild) {
- invariant(
- child !== beforeChild,
- 'ReactART: Can not insert node before itself',
- );
- child.injectBefore(beforeChild);
- },
-
prepareForCommit() {
// Noop
},
@@ -494,16 +452,6 @@ const ARTRenderer = ReactFiberReconciler({
return UPDATE_SIGNAL;
},
- removeChild(parentInstance, child) {
- destroyEventListeners(child);
- child.eject();
- },
-
- removeChildFromContainer(parentInstance, child) {
- destroyEventListeners(child);
- child.eject();
- },
-
resetAfterCommit() {
// Noop
},
@@ -535,6 +483,60 @@ const ARTRenderer = ReactFiberReconciler({
now: ReactDOMFrameScheduling.now,
useSyncScheduling: true,
+
+ mutation: {
+ appendChild(parentInstance, child) {
+ if (child.parentNode === parentInstance) {
+ child.eject();
+ }
+ child.inject(parentInstance);
+ },
+
+ appendChildToContainer(parentInstance, child) {
+ if (child.parentNode === parentInstance) {
+ child.eject();
+ }
+ child.inject(parentInstance);
+ },
+
+ insertBefore(parentInstance, child, beforeChild) {
+ invariant(
+ child !== beforeChild,
+ 'ReactART: Can not insert node before itself',
+ );
+ child.injectBefore(beforeChild);
+ },
+
+ insertInContainerBefore(parentInstance, child, beforeChild) {
+ invariant(
+ child !== beforeChild,
+ 'ReactART: Can not insert node before itself',
+ );
+ child.injectBefore(beforeChild);
+ },
+
+ removeChild(parentInstance, child) {
+ destroyEventListeners(child);
+ child.eject();
+ },
+
+ removeChildFromContainer(parentInstance, child) {
+ destroyEventListeners(child);
+ child.eject();
+ },
+
+ commitTextUpdate(textInstance, oldText, newText) {
+ // Noop
+ },
+
+ commitMount(instance, type, newProps) {
+ // Noop
+ },
+
+ commitUpdate(instance, updatePayload, type, oldProps, newProps) {
+ instance._applyProps(instance, newProps, oldProps);
+ },
+ },
});
/** API */
diff --git a/src/renderers/dom/fiber/ReactDOMFiberEntry.js b/src/renderers/dom/fiber/ReactDOMFiberEntry.js
index 0bd998e288..1bdb813b1e 100644
--- a/src/renderers/dom/fiber/ReactDOMFiberEntry.js
+++ b/src/renderers/dom/fiber/ReactDOMFiberEntry.js
@@ -317,34 +317,6 @@ var DOMRenderer = ReactFiberReconciler({
);
},
- commitMount(
- domElement: Instance,
- type: string,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- ((domElement: any):
- | HTMLButtonElement
- | HTMLInputElement
- | HTMLSelectElement
- | HTMLTextAreaElement).focus();
- },
-
- commitUpdate(
- domElement: Instance,
- updatePayload: Array,
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- // Update the props handle so that we know which props are the ones with
- // with current event handlers.
- updateFiberProps(domElement, newProps);
- // Apply the diff to the DOM node.
- updateProperties(domElement, updatePayload, type, oldProps, newProps);
- },
-
shouldSetTextContent(type: string, props: Props): boolean {
return (
type === 'textarea' ||
@@ -356,10 +328,6 @@ var DOMRenderer = ReactFiberReconciler({
);
},
- resetTextContent(domElement: Instance): void {
- domElement.textContent = '';
- },
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return !!props.hidden;
},
@@ -379,245 +347,287 @@ var DOMRenderer = ReactFiberReconciler({
return textNode;
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string,
- ): void {
- textInstance.nodeValue = newText;
- },
-
- appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
- parentInstance.appendChild(child);
- },
-
- appendChildToContainer(
- container: Container,
- child: Instance | TextInstance,
- ): void {
- if (container.nodeType === COMMENT_NODE) {
- (container.parentNode: any).insertBefore(child, container);
- } else {
- container.appendChild(child);
- }
- },
-
- insertBefore(
- parentInstance: Instance,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- parentInstance.insertBefore(child, beforeChild);
- },
-
- insertInContainerBefore(
- container: Container,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- if (container.nodeType === COMMENT_NODE) {
- (container.parentNode: any).insertBefore(child, beforeChild);
- } else {
- container.insertBefore(child, beforeChild);
- }
- },
-
- removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
- parentInstance.removeChild(child);
- },
-
- removeChildFromContainer(
- container: Container,
- child: Instance | TextInstance,
- ): void {
- if (container.nodeType === COMMENT_NODE) {
- (container.parentNode: any).removeChild(child);
- } else {
- container.removeChild(child);
- }
- },
-
now: ReactDOMFrameScheduling.now,
- canHydrateInstance(
- instance: Instance | TextInstance,
- type: string,
- props: Props,
- ): boolean {
- return (
- instance.nodeType === ELEMENT_NODE &&
- type.toLowerCase() === instance.nodeName.toLowerCase()
- );
- },
+ mutation: {
+ commitMount(
+ domElement: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ ((domElement: any):
+ | HTMLButtonElement
+ | HTMLInputElement
+ | HTMLSelectElement
+ | HTMLTextAreaElement).focus();
+ },
- canHydrateTextInstance(
- instance: Instance | TextInstance,
- text: string,
- ): boolean {
- if (text === '') {
- // Empty strings are not parsed by HTML so there won't be a correct match here.
- return false;
- }
- return instance.nodeType === TEXT_NODE;
- },
+ commitUpdate(
+ domElement: Instance,
+ updatePayload: Array,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ // Update the props handle so that we know which props are the ones with
+ // with current event handlers.
+ updateFiberProps(domElement, newProps);
+ // Apply the diff to the DOM node.
+ updateProperties(domElement, updatePayload, type, oldProps, newProps);
+ },
- getNextHydratableSibling(
- instance: Instance | TextInstance,
- ): null | Instance | TextInstance {
- let node = instance.nextSibling;
- // Skip non-hydratable nodes.
- while (
- node &&
- node.nodeType !== ELEMENT_NODE &&
- node.nodeType !== TEXT_NODE
- ) {
- node = node.nextSibling;
- }
- return (node: any);
- },
+ resetTextContent(domElement: Instance): void {
+ domElement.textContent = '';
+ },
- getFirstHydratableChild(
- parentInstance: Container | Instance,
- ): null | Instance | TextInstance {
- let next = parentInstance.firstChild;
- // Skip non-hydratable nodes.
- while (
- next &&
- next.nodeType !== ELEMENT_NODE &&
- next.nodeType !== TEXT_NODE
- ) {
- next = next.nextSibling;
- }
- return (next: any);
- },
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+ ): void {
+ textInstance.nodeValue = newText;
+ },
- hydrateInstance(
- instance: Instance,
- type: string,
- props: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): null | Array {
- precacheFiberNode(internalInstanceHandle, instance);
- // TODO: Possibly defer this until the commit phase where all the events
- // get attached.
- updateFiberProps(instance, props);
- let parentNamespace: string;
- if (__DEV__) {
- const hostContextDev = ((hostContext: any): HostContextDev);
- parentNamespace = hostContextDev.namespace;
- } else {
- parentNamespace = ((hostContext: any): HostContextProd);
- }
- return diffHydratedProperties(
- instance,
- type,
- props,
- parentNamespace,
- rootContainerInstance,
- );
- },
+ appendChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ parentInstance.appendChild(child);
+ },
- hydrateTextInstance(
- textInstance: TextInstance,
- text: string,
- internalInstanceHandle: Object,
- ): boolean {
- precacheFiberNode(internalInstanceHandle, textInstance);
- return diffHydratedText(textInstance, text);
- },
-
- didNotMatchHydratedContainerTextInstance(
- parentContainer: Container,
- textInstance: TextInstance,
- text: string,
- ) {
- if (__DEV__) {
- warnForUnmatchedText(textInstance, text);
- }
- },
-
- didNotMatchHydratedTextInstance(
- parentType: string,
- parentProps: Props,
- parentInstance: Instance,
- textInstance: TextInstance,
- text: string,
- ) {
- if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
- warnForUnmatchedText(textInstance, text);
- }
- },
-
- didNotHydrateContainerInstance(
- parentContainer: Container,
- instance: Instance | TextInstance,
- ) {
- if (__DEV__) {
- if (instance.nodeType === 1) {
- warnForDeletedHydratableElement(parentContainer, (instance: any));
+ appendChildToContainer(
+ container: Container,
+ child: Instance | TextInstance,
+ ): void {
+ if (container.nodeType === COMMENT_NODE) {
+ (container.parentNode: any).insertBefore(child, container);
} else {
- warnForDeletedHydratableText(parentContainer, (instance: any));
+ container.appendChild(child);
}
- }
- },
+ },
- didNotHydrateInstance(
- parentType: string,
- parentProps: Props,
- parentInstance: Instance,
- instance: Instance | TextInstance,
- ) {
- if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
- if (instance.nodeType === 1) {
- warnForDeletedHydratableElement(parentInstance, (instance: any));
+ insertBefore(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ parentInstance.insertBefore(child, beforeChild);
+ },
+
+ insertInContainerBefore(
+ container: Container,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ if (container.nodeType === COMMENT_NODE) {
+ (container.parentNode: any).insertBefore(child, beforeChild);
} else {
- warnForDeletedHydratableText(parentInstance, (instance: any));
+ container.insertBefore(child, beforeChild);
}
- }
+ },
+
+ removeChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ parentInstance.removeChild(child);
+ },
+
+ removeChildFromContainer(
+ container: Container,
+ child: Instance | TextInstance,
+ ): void {
+ if (container.nodeType === COMMENT_NODE) {
+ (container.parentNode: any).removeChild(child);
+ } else {
+ container.removeChild(child);
+ }
+ },
},
- didNotFindHydratableContainerInstance(
- parentContainer: Container,
- type: string,
- props: Props,
- ) {
- if (__DEV__) {
- warnForInsertedHydratedElement(parentContainer, type, props);
- }
- },
+ hydration: {
+ canHydrateInstance(
+ instance: Instance | TextInstance,
+ type: string,
+ props: Props,
+ ): boolean {
+ return (
+ instance.nodeType === ELEMENT_NODE &&
+ type.toLowerCase() === instance.nodeName.toLowerCase()
+ );
+ },
- didNotFindHydratableContainerTextInstance(
- parentContainer: Container,
- text: string,
- ) {
- if (__DEV__) {
- warnForInsertedHydratedText(parentContainer, text);
- }
- },
+ canHydrateTextInstance(
+ instance: Instance | TextInstance,
+ text: string,
+ ): boolean {
+ if (text === '') {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return false;
+ }
+ return instance.nodeType === TEXT_NODE;
+ },
- didNotFindHydratableInstance(
- parentType: string,
- parentProps: Props,
- parentInstance: Instance,
- type: string,
- props: Props,
- ) {
- if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
- warnForInsertedHydratedElement(parentInstance, type, props);
- }
- },
+ getNextHydratableSibling(
+ instance: Instance | TextInstance,
+ ): null | Instance | TextInstance {
+ let node = instance.nextSibling;
+ // Skip non-hydratable nodes.
+ while (
+ node &&
+ node.nodeType !== ELEMENT_NODE &&
+ node.nodeType !== TEXT_NODE
+ ) {
+ node = node.nextSibling;
+ }
+ return (node: any);
+ },
- didNotFindHydratableTextInstance(
- parentType: string,
- parentProps: Props,
- parentInstance: Instance,
- text: string,
- ) {
- if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
- warnForInsertedHydratedText(parentInstance, text);
- }
+ getFirstHydratableChild(
+ parentInstance: Container | Instance,
+ ): null | Instance | TextInstance {
+ let next = parentInstance.firstChild;
+ // Skip non-hydratable nodes.
+ while (
+ next &&
+ next.nodeType !== ELEMENT_NODE &&
+ next.nodeType !== TEXT_NODE
+ ) {
+ next = next.nextSibling;
+ }
+ return (next: any);
+ },
+
+ hydrateInstance(
+ instance: Instance,
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+ ): null | Array {
+ precacheFiberNode(internalInstanceHandle, instance);
+ // TODO: Possibly defer this until the commit phase where all the events
+ // get attached.
+ updateFiberProps(instance, props);
+ let parentNamespace: string;
+ if (__DEV__) {
+ const hostContextDev = ((hostContext: any): HostContextDev);
+ parentNamespace = hostContextDev.namespace;
+ } else {
+ parentNamespace = ((hostContext: any): HostContextProd);
+ }
+ return diffHydratedProperties(
+ instance,
+ type,
+ props,
+ parentNamespace,
+ rootContainerInstance,
+ );
+ },
+
+ hydrateTextInstance(
+ textInstance: TextInstance,
+ text: string,
+ internalInstanceHandle: Object,
+ ): boolean {
+ precacheFiberNode(internalInstanceHandle, textInstance);
+ return diffHydratedText(textInstance, text);
+ },
+
+ didNotMatchHydratedContainerTextInstance(
+ parentContainer: Container,
+ textInstance: TextInstance,
+ text: string,
+ ) {
+ if (__DEV__) {
+ warnForUnmatchedText(textInstance, text);
+ }
+ },
+
+ didNotMatchHydratedTextInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ textInstance: TextInstance,
+ text: string,
+ ) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForUnmatchedText(textInstance, text);
+ }
+ },
+
+ didNotHydrateContainerInstance(
+ parentContainer: Container,
+ instance: Instance | TextInstance,
+ ) {
+ if (__DEV__) {
+ if (instance.nodeType === 1) {
+ warnForDeletedHydratableElement(parentContainer, (instance: any));
+ } else {
+ warnForDeletedHydratableText(parentContainer, (instance: any));
+ }
+ }
+ },
+
+ didNotHydrateInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ instance: Instance | TextInstance,
+ ) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ if (instance.nodeType === 1) {
+ warnForDeletedHydratableElement(parentInstance, (instance: any));
+ } else {
+ warnForDeletedHydratableText(parentInstance, (instance: any));
+ }
+ }
+ },
+
+ didNotFindHydratableContainerInstance(
+ parentContainer: Container,
+ type: string,
+ props: Props,
+ ) {
+ if (__DEV__) {
+ warnForInsertedHydratedElement(parentContainer, type, props);
+ }
+ },
+
+ didNotFindHydratableContainerTextInstance(
+ parentContainer: Container,
+ text: string,
+ ) {
+ if (__DEV__) {
+ warnForInsertedHydratedText(parentContainer, text);
+ }
+ },
+
+ didNotFindHydratableInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ type: string,
+ props: Props,
+ ) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedElement(parentInstance, type, props);
+ }
+ },
+
+ didNotFindHydratableTextInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ text: string,
+ ) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedText(parentInstance, text);
+ }
+ },
},
scheduleDeferredCallback: ReactDOMFrameScheduling.rIC,
diff --git a/src/renderers/native-cs/ReactNativeCSFiberEntry.js b/src/renderers/native-cs/ReactNativeCSFiberEntry.js
new file mode 100644
index 0000000000..72ceb060c7
--- /dev/null
+++ b/src/renderers/native-cs/ReactNativeCSFiberEntry.js
@@ -0,0 +1,228 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @providesModule ReactNativeCSFiberEntry
+ * @flow
+ */
+
+'use strict';
+
+const ReactGenericBatching = require('ReactGenericBatching');
+const ReactVersion = require('ReactVersion');
+
+const {injectInternals} = require('ReactFiberDevToolsHook');
+
+import type {ReactNativeCSType} from 'ReactNativeCSTypes';
+
+const ReactFiberReconciler = require('ReactFiberReconciler');
+
+const emptyObject = require('fbjs/lib/emptyObject');
+
+export type Container = number;
+export type Instance = number;
+export type Props = Object;
+export type TextInstance = number;
+
+function processProps(instance: number, props: Props): Object {
+ const propsPayload = {};
+ for (var key in props) {
+ if (key === 'children') {
+ // Skip special case.
+ continue;
+ }
+ var value = props[key];
+ if (typeof value === 'function') {
+ value = {
+ style: 'rt-event',
+ event: key,
+ tag: instance,
+ };
+ }
+ propsPayload[key] = value;
+ }
+ return propsPayload;
+}
+
+function arePropsEqual(oldProps: Props, newProps: Props): boolean {
+ var key;
+ for (key in newProps) {
+ if (key === 'children') {
+ // Skip special case.
+ continue;
+ }
+ if (newProps[key] !== oldProps[key]) {
+ return false;
+ }
+ }
+ for (key in oldProps) {
+ if (key === 'children') {
+ // Skip special case.
+ continue;
+ }
+ if (!(key in newProps)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+const ReactNativeCSFiberRenderer = ReactFiberReconciler({
+ appendInitialChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {},
+
+ createInstance(
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: {},
+ internalInstanceHandle: Object,
+ ): Instance {
+ return 0;
+ },
+
+ createTextInstance(
+ text: string,
+ rootContainerInstance: Container,
+ hostContext: {},
+ internalInstanceHandle: Object,
+ ): TextInstance {
+ return 0;
+ },
+
+ finalizeInitialChildren(
+ parentInstance: Instance,
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ ): boolean {
+ return false;
+ },
+
+ getRootHostContext(): {} {
+ return emptyObject;
+ },
+
+ getChildHostContext(): {} {
+ return emptyObject;
+ },
+
+ getPublicInstance(instance) {
+ return instance;
+ },
+
+ prepareForCommit(): void {},
+
+ prepareUpdate(
+ instance: Instance,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ hostContext: {},
+ ): null | Object {
+ if (arePropsEqual(oldProps, newProps)) {
+ return null;
+ }
+ return processProps(instance, newProps);
+ },
+
+ resetAfterCommit(): void {},
+
+ shouldDeprioritizeSubtree(type: string, props: Props): boolean {
+ return false;
+ },
+
+ scheduleDeferredCallback: global.requestIdleCallback,
+
+ shouldSetTextContent(type: string, props: Props): boolean {
+ // TODO: Figure out when we should allow text content.
+ return false;
+ },
+
+ useSyncScheduling: true,
+
+ now(): number {
+ // TODO: Enable expiration by implementing this method.
+ return 0;
+ },
+
+ persistence: {
+ cloneInstance(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ keepChildren: boolean,
+ ): Instance {
+ return 0;
+ },
+ tryToReuseInstance(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ keepChildren: boolean,
+ ): Instance {
+ return 0;
+ },
+
+ createRootInstance(
+ rootContainerInstance: Container,
+ hostContext: {},
+ ): Instance {
+ return 123;
+ },
+ commitRootInstance(rootInstance: Instance): void {},
+ },
+});
+
+const roots = new Map();
+
+const ReactNativeCSFiber: ReactNativeCSType = {
+ render(element: React$Element, containerTag: any, callback: ?Function) {
+ let root = roots.get(containerTag);
+
+ if (!root) {
+ // TODO (bvaughn): If we decide to keep the wrapper component,
+ // We could create a wrapper for containerTag as well to reduce special casing.
+ root = ReactNativeCSFiberRenderer.createContainer(containerTag);
+ roots.set(containerTag, root);
+ }
+ ReactNativeCSFiberRenderer.updateContainer(element, root, null, callback);
+
+ return ReactNativeCSFiberRenderer.getPublicRootInstance(root);
+ },
+
+ unmountComponentAtNode(containerTag: number) {
+ const root = roots.get(containerTag);
+ if (root) {
+ // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
+ ReactNativeCSFiberRenderer.updateContainer(null, root, null, () => {
+ roots.delete(containerTag);
+ });
+ }
+ },
+
+ unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
+
+ flushSync: ReactNativeCSFiberRenderer.flushSync,
+};
+
+injectInternals({
+ findHostInstanceByFiber: ReactNativeCSFiberRenderer.findHostInstance,
+ // This is an enum because we may add more (e.g. profiler build)
+ bundleType: __DEV__ ? 1 : 0,
+ version: ReactVersion,
+ rendererPackageName: 'react-native-cs',
+});
+
+module.exports = ReactNativeCSFiber;
diff --git a/src/renderers/native-cs/ReactNativeCSTypes.js b/src/renderers/native-cs/ReactNativeCSTypes.js
new file mode 100644
index 0000000000..85c5025dbd
--- /dev/null
+++ b/src/renderers/native-cs/ReactNativeCSTypes.js
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @providesModule ReactNativeCSTypes
+ * @flow
+ */
+'use strict';
+
+/**
+ * Flat CS renderer bundles are too big for Flow to parse efficiently.
+ * Provide minimal Flow typing for the high-level RN API and call it a day.
+ */
+export type ReactNativeCSType = {
+ render(
+ element: React$Element,
+ containerTag: any,
+ callback: ?Function,
+ ): any,
+ unmountComponentAtNode(containerTag: number): any,
+ unstable_batchedUpdates: any, // TODO (bvaughn) Add types
+};
diff --git a/src/renderers/native-cs/__tests__/ReactNativeCS-test.js b/src/renderers/native-cs/__tests__/ReactNativeCS-test.js
new file mode 100644
index 0000000000..f04074529b
--- /dev/null
+++ b/src/renderers/native-cs/__tests__/ReactNativeCS-test.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+var React;
+var ReactNativeCS;
+
+describe('ReactNativeCS', () => {
+ beforeEach(() => {
+ jest.resetModules();
+
+ React = require('react');
+ ReactNativeCS = require('ReactNativeCSFiberEntry');
+ });
+
+ it('should be able to create and render a native component', () => {
+ const RCTView = 'View';
+ ReactNativeCS.render(, 1);
+ });
+});
diff --git a/src/renderers/native-rt/ReactNativeRTFiberRenderer.js b/src/renderers/native-rt/ReactNativeRTFiberRenderer.js
index 443c84b1fe..92d42756a0 100644
--- a/src/renderers/native-rt/ReactNativeRTFiberRenderer.js
+++ b/src/renderers/native-rt/ReactNativeRTFiberRenderer.js
@@ -69,17 +69,6 @@ function arePropsEqual(oldProps: Props, newProps: Props): boolean {
}
const NativeRTRenderer = ReactFiberReconciler({
- appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
- RTManager.appendChild(parentInstance, child);
- },
-
- appendChildToContainer(
- parentInstance: Container,
- child: Instance | TextInstance,
- ): void {
- RTManager.appendChildToContext(parentInstance, child);
- },
-
appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
@@ -87,35 +76,6 @@ const NativeRTRenderer = ReactFiberReconciler({
RTManager.appendChild(parentInstance, child);
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string,
- ): void {
- invariant(false, 'Text components are not yet supported.');
- },
-
- commitMount(
- instance: Instance,
- type: string,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- // Noop
- },
-
- commitUpdate(
- instance: Instance,
- updatePayload: Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- updateFiberProps(instance, newProps);
- RTManager.updateNode(instance, updatePayload);
- },
-
createInstance(
type: string,
props: Props,
@@ -160,22 +120,6 @@ const NativeRTRenderer = ReactFiberReconciler({
return instance;
},
- insertBefore(
- parentInstance: Instance,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- RTManager.prependChild(child, beforeChild);
- },
-
- insertInContainerBefore(
- parentInstance: Container,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- RTManager.prependChild(child, beforeChild);
- },
-
prepareForCommit(): void {
RTManager.beginUpdates();
},
@@ -194,27 +138,10 @@ const NativeRTRenderer = ReactFiberReconciler({
return processProps(instance, newProps);
},
- removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
- // TODO: recursively uncache, by traversing fibers, this will currently leak
- RTManager.deleteChild(child);
- },
-
- removeChildFromContainer(
- parentInstance: Container,
- child: Instance | TextInstance,
- ): void {
- // TODO: recursively uncache, by traversing fibers, this will currently leak
- RTManager.deleteChild(child);
- },
-
resetAfterCommit(): void {
RTManager.completeUpdates();
},
- resetTextContent(instance: Instance): void {
- // Noop
- },
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
@@ -232,6 +159,87 @@ const NativeRTRenderer = ReactFiberReconciler({
// TODO: Enable expiration by implementing this method.
return 0;
},
+
+ mutation: {
+ appendChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ RTManager.appendChild(parentInstance, child);
+ },
+
+ appendChildToContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ ): void {
+ RTManager.appendChildToContext(parentInstance, child);
+ },
+
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+ ): void {
+ invariant(false, 'Text components are not yet supported.');
+ },
+
+ commitMount(
+ instance: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ // Noop
+ },
+
+ commitUpdate(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ updateFiberProps(instance, newProps);
+ RTManager.updateNode(instance, updatePayload);
+ },
+
+ insertBefore(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ RTManager.prependChild(child, beforeChild);
+ },
+
+ insertInContainerBefore(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ RTManager.prependChild(child, beforeChild);
+ },
+
+ removeChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ // TODO: recursively uncache, by traversing fibers, this will currently leak
+ RTManager.deleteChild(child);
+ },
+
+ removeChildFromContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ ): void {
+ // TODO: recursively uncache, by traversing fibers, this will currently leak
+ RTManager.deleteChild(child);
+ },
+
+ resetTextContent(instance: Instance): void {
+ // Noop
+ },
+ },
});
module.exports = NativeRTRenderer;
diff --git a/src/renderers/native/ReactNativeFiberRenderer.js b/src/renderers/native/ReactNativeFiberRenderer.js
index 859f422f4c..8c24b30da7 100644
--- a/src/renderers/native/ReactNativeFiberRenderer.js
+++ b/src/renderers/native/ReactNativeFiberRenderer.js
@@ -51,48 +51,6 @@ function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
}
const NativeRenderer = ReactFiberReconciler({
- appendChild(parentInstance: Instance, child: Instance | TextInstance): void {
- const childTag = typeof child === 'number' ? child : child._nativeTag;
- const children = parentInstance._children;
- const index = children.indexOf(child);
-
- if (index >= 0) {
- children.splice(index, 1);
- children.push(child);
-
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerTag
- [index], // moveFromIndices
- [children.length - 1], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [], // removeAtIndices
- );
- } else {
- children.push(child);
-
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerTag
- [], // moveFromIndices
- [], // moveToIndices
- [childTag], // addChildReactTags
- [children.length - 1], // addAtIndices
- [], // removeAtIndices
- );
- }
- },
-
- appendChildToContainer(
- parentInstance: Container,
- child: Instance | TextInstance,
- ): void {
- const childTag = typeof child === 'number' ? child : child._nativeTag;
- UIManager.setChildren(
- parentInstance, // containerTag
- [childTag], // reactTags
- );
- },
-
appendInitialChild(
parentInstance: Instance,
child: Instance | TextInstance,
@@ -100,57 +58,6 @@ const NativeRenderer = ReactFiberReconciler({
parentInstance._children.push(child);
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string,
- ): void {
- UIManager.updateView(
- textInstance, // reactTag
- 'RCTRawText', // viewName
- {text: newText}, // props
- );
- },
-
- commitMount(
- instance: Instance,
- type: string,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- // Noop
- },
-
- commitUpdate(
- instance: Instance,
- updatePayloadTODO: Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- const viewConfig = instance.viewConfig;
-
- updateFiberProps(instance._nativeTag, newProps);
-
- const updatePayload = ReactNativeAttributePayload.diff(
- oldProps,
- newProps,
- viewConfig.validAttributes,
- );
-
- // Avoid the overhead of bridge calls if there's no update.
- // This is an expensive no-op for Android, and causes an unnecessary
- // view invalidation for certain components (eg RCTTextInput) on iOS.
- if (updatePayload != null) {
- UIManager.updateView(
- instance._nativeTag, // reactTag
- viewConfig.uiViewClassName, // viewName
- updatePayload, // props
- );
- }
- },
-
createInstance(
type: string,
props: Props,
@@ -251,60 +158,6 @@ const NativeRenderer = ReactFiberReconciler({
return instance;
},
- insertBefore(
- parentInstance: Instance,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- const children = (parentInstance: any)._children;
- const index = children.indexOf(child);
-
- // Move existing child or add new child?
- if (index >= 0) {
- children.splice(index, 1);
- const beforeChildIndex = children.indexOf(beforeChild);
- children.splice(beforeChildIndex, 0, child);
-
- UIManager.manageChildren(
- (parentInstance: any)._nativeTag, // containerID
- [index], // moveFromIndices
- [beforeChildIndex], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [], // removeAtIndices
- );
- } else {
- const beforeChildIndex = children.indexOf(beforeChild);
- children.splice(beforeChildIndex, 0, child);
-
- const childTag = typeof child === 'number' ? child : child._nativeTag;
-
- UIManager.manageChildren(
- (parentInstance: any)._nativeTag, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [childTag], // addChildReactTags
- [beforeChildIndex], // addAtIndices
- [], // removeAtIndices
- );
- }
- },
-
- insertInContainerBefore(
- parentInstance: Container,
- child: Instance | TextInstance,
- beforeChild: Instance | TextInstance,
- ): void {
- // TODO (bvaughn): Remove this check when...
- // We create a wrapper object for the container in ReactNative render()
- // Or we refactor to remove wrapper objects entirely.
- // For more info on pros/cons see PR #8560 description.
- invariant(
- typeof parentInstance !== 'number',
- 'Container does not support insertBefore operation',
- );
- },
-
prepareForCommit(): void {
// Noop
},
@@ -320,46 +173,10 @@ const NativeRenderer = ReactFiberReconciler({
return emptyObject;
},
- removeChild(parentInstance: Instance, child: Instance | TextInstance): void {
- recursivelyUncacheFiberNode(child);
- const children = parentInstance._children;
- const index = children.indexOf(child);
-
- children.splice(index, 1);
-
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [index], // removeAtIndices
- );
- },
-
- removeChildFromContainer(
- parentInstance: Container,
- child: Instance | TextInstance,
- ): void {
- recursivelyUncacheFiberNode(child);
- UIManager.manageChildren(
- parentInstance, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [0], // removeAtIndices
- );
- },
-
resetAfterCommit(): void {
// Noop
},
- resetTextContent(instance: Instance): void {
- // Noop
- },
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
@@ -382,6 +199,197 @@ const NativeRenderer = ReactFiberReconciler({
// TODO: Enable expiration by implementing this method.
return 0;
},
+
+ mutation: {
+ appendChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ const childTag = typeof child === 'number' ? child : child._nativeTag;
+ const children = parentInstance._children;
+ const index = children.indexOf(child);
+
+ if (index >= 0) {
+ children.splice(index, 1);
+ children.push(child);
+
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerTag
+ [index], // moveFromIndices
+ [children.length - 1], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [], // removeAtIndices
+ );
+ } else {
+ children.push(child);
+
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerTag
+ [], // moveFromIndices
+ [], // moveToIndices
+ [childTag], // addChildReactTags
+ [children.length - 1], // addAtIndices
+ [], // removeAtIndices
+ );
+ }
+ },
+
+ appendChildToContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ ): void {
+ const childTag = typeof child === 'number' ? child : child._nativeTag;
+ UIManager.setChildren(
+ parentInstance, // containerTag
+ [childTag], // reactTags
+ );
+ },
+
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+ ): void {
+ UIManager.updateView(
+ textInstance, // reactTag
+ 'RCTRawText', // viewName
+ {text: newText}, // props
+ );
+ },
+
+ commitMount(
+ instance: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ // Noop
+ },
+
+ commitUpdate(
+ instance: Instance,
+ updatePayloadTODO: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ const viewConfig = instance.viewConfig;
+
+ updateFiberProps(instance._nativeTag, newProps);
+
+ const updatePayload = ReactNativeAttributePayload.diff(
+ oldProps,
+ newProps,
+ viewConfig.validAttributes,
+ );
+
+ // Avoid the overhead of bridge calls if there's no update.
+ // This is an expensive no-op for Android, and causes an unnecessary
+ // view invalidation for certain components (eg RCTTextInput) on iOS.
+ if (updatePayload != null) {
+ UIManager.updateView(
+ instance._nativeTag, // reactTag
+ viewConfig.uiViewClassName, // viewName
+ updatePayload, // props
+ );
+ }
+ },
+
+ insertBefore(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ const children = (parentInstance: any)._children;
+ const index = children.indexOf(child);
+
+ // Move existing child or add new child?
+ if (index >= 0) {
+ children.splice(index, 1);
+ const beforeChildIndex = children.indexOf(beforeChild);
+ children.splice(beforeChildIndex, 0, child);
+
+ UIManager.manageChildren(
+ (parentInstance: any)._nativeTag, // containerID
+ [index], // moveFromIndices
+ [beforeChildIndex], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [], // removeAtIndices
+ );
+ } else {
+ const beforeChildIndex = children.indexOf(beforeChild);
+ children.splice(beforeChildIndex, 0, child);
+
+ const childTag = typeof child === 'number' ? child : child._nativeTag;
+
+ UIManager.manageChildren(
+ (parentInstance: any)._nativeTag, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [childTag], // addChildReactTags
+ [beforeChildIndex], // addAtIndices
+ [], // removeAtIndices
+ );
+ }
+ },
+
+ insertInContainerBefore(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+ ): void {
+ // TODO (bvaughn): Remove this check when...
+ // We create a wrapper object for the container in ReactNative render()
+ // Or we refactor to remove wrapper objects entirely.
+ // For more info on pros/cons see PR #8560 description.
+ invariant(
+ typeof parentInstance !== 'number',
+ 'Container does not support insertBefore operation',
+ );
+ },
+
+ removeChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ ): void {
+ recursivelyUncacheFiberNode(child);
+ const children = parentInstance._children;
+ const index = children.indexOf(child);
+
+ children.splice(index, 1);
+
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [index], // removeAtIndices
+ );
+ },
+
+ removeChildFromContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+ ): void {
+ recursivelyUncacheFiberNode(child);
+ UIManager.manageChildren(
+ parentInstance, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [0], // removeAtIndices
+ );
+ },
+
+ resetTextContent(instance: Instance): void {
+ // Noop
+ },
+ },
});
module.exports = NativeRenderer;
diff --git a/src/renderers/noop/ReactNoopEntry.js b/src/renderers/noop/ReactNoopEntry.js
index 6a5ed0b11e..bf964eb84e 100644
--- a/src/renderers/noop/ReactNoopEntry.js
+++ b/src/renderers/noop/ReactNoopEntry.js
@@ -137,28 +137,12 @@ var NoopRenderer = ReactFiberReconciler({
return UPDATE_SIGNAL;
},
- commitMount(instance: Instance, type: string, newProps: Props): void {
- // Noop
- },
-
- commitUpdate(
- instance: Instance,
- updatePayload: Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- ): void {
- instance.prop = newProps.prop;
- },
-
shouldSetTextContent(type: string, props: Props): boolean {
return (
typeof props.children === 'string' || typeof props.children === 'number'
);
},
- resetTextContent(instance: Instance): void {},
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return !!props.hidden;
},
@@ -175,21 +159,6 @@ var NoopRenderer = ReactFiberReconciler({
return inst;
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string,
- ): void {
- textInstance.text = newText;
- },
-
- appendChild: appendChild,
- appendChildToContainer: appendChild,
- insertBefore: insertBefore,
- insertInContainerBefore: insertBefore,
- removeChild: removeChild,
- removeChildFromContainer: removeChild,
-
scheduleDeferredCallback(callback) {
if (scheduledCallback) {
throw new Error(
@@ -207,6 +176,39 @@ var NoopRenderer = ReactFiberReconciler({
now(): number {
return elapsedTimeInMs;
},
+
+ mutation: {
+ commitMount(instance: Instance, type: string, newProps: Props): void {
+ // Noop
+ },
+
+ commitUpdate(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ ): void {
+ instance.prop = newProps.prop;
+ },
+
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+ ): void {
+ textInstance.text = newText;
+ },
+
+ appendChild: appendChild,
+ appendChildToContainer: appendChild,
+ insertBefore: insertBefore,
+ insertInContainerBefore: insertBefore,
+ removeChild: removeChild,
+ removeChildFromContainer: removeChild,
+
+ resetTextContent(instance: Instance): void {},
+ },
});
var rootContainers = new Map();
diff --git a/src/renderers/shared/fiber/ReactFiberCommitWork.js b/src/renderers/shared/fiber/ReactFiberCommitWork.js
index 98a8f3def9..32e7fd9bc7 100644
--- a/src/renderers/shared/fiber/ReactFiberCommitWork.js
+++ b/src/renderers/shared/fiber/ReactFiberCommitWork.js
@@ -47,19 +47,7 @@ module.exports = function(
config: HostConfig,
captureError: (failedFiber: Fiber, error: mixed) => Fiber | null,
) {
- const {
- commitMount,
- commitUpdate,
- resetTextContent,
- commitTextUpdate,
- appendChild,
- appendChildToContainer,
- insertBefore,
- insertInContainerBefore,
- removeChild,
- removeChildFromContainer,
- getPublicInstance,
- } = config;
+ const {getPublicInstance} = config;
if (__DEV__) {
var callComponentWillUnmountWithTimerInDev = function(current, instance) {
@@ -115,6 +103,213 @@ module.exports = function(
}
}
+ function commitLifeCycles(current: Fiber | null, finishedWork: Fiber): void {
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ const instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current === null) {
+ if (__DEV__) {
+ startPhaseTimer(finishedWork, 'componentDidMount');
+ }
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ instance.componentDidMount();
+ if (__DEV__) {
+ stopPhaseTimer();
+ }
+ } else {
+ const prevProps = current.memoizedProps;
+ const prevState = current.memoizedState;
+ if (__DEV__) {
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
+ }
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ instance.componentDidUpdate(prevProps, prevState);
+ if (__DEV__) {
+ stopPhaseTimer();
+ }
+ }
+ }
+ if (
+ finishedWork.effectTag & Callback &&
+ finishedWork.updateQueue !== null
+ ) {
+ commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
+ }
+ return;
+ }
+ case HostRoot: {
+ const updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ const instance = finishedWork.child && finishedWork.child.stateNode;
+ commitCallbacks(finishedWork, updateQueue, instance);
+ }
+ return;
+ }
+ case HostComponent: {
+ const instance: I = finishedWork.stateNode;
+
+ // Renderers may schedule work to be done after host components are mounted
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
+ // These effects should only be committed when components are first mounted,
+ // aka when there is no current/alternate.
+ if (current === null && finishedWork.effectTag & Update) {
+ const type = finishedWork.type;
+ const props = finishedWork.memoizedProps;
+ commitMount(instance, type, props, finishedWork);
+ }
+
+ return;
+ }
+ case HostText: {
+ // We have no life-cycles associated with text.
+ return;
+ }
+ case HostPortal: {
+ // We have no life-cycles associated with portals.
+ return;
+ }
+ default: {
+ invariant(
+ false,
+ 'This unit of work tag should not have side-effects. This error is ' +
+ 'likely caused by a bug in React. Please file an issue.',
+ );
+ }
+ }
+ }
+
+ function commitAttachRef(finishedWork: Fiber) {
+ const ref = finishedWork.ref;
+ if (ref !== null) {
+ const instance = finishedWork.stateNode;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ ref(getPublicInstance(instance));
+ break;
+ default:
+ ref(instance);
+ }
+ }
+ }
+
+ function commitDetachRef(current: Fiber) {
+ const currentRef = current.ref;
+ if (currentRef !== null) {
+ currentRef(null);
+ }
+ }
+
+ // User-originating errors (lifecycles and refs) should not interrupt
+ // deletion, so don't let them throw. Host-originating errors should
+ // interrupt deletion, so it's okay
+ function commitUnmount(current: Fiber): void {
+ if (typeof onCommitUnmount === 'function') {
+ onCommitUnmount(current);
+ }
+
+ switch (current.tag) {
+ case ClassComponent: {
+ safelyDetachRef(current);
+ const instance = current.stateNode;
+ if (typeof instance.componentWillUnmount === 'function') {
+ safelyCallComponentWillUnmount(current, instance);
+ }
+ return;
+ }
+ case HostComponent: {
+ safelyDetachRef(current);
+ return;
+ }
+ case CoroutineComponent: {
+ commitNestedUnmounts(current.stateNode);
+ return;
+ }
+ case HostPortal: {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ unmountHostComponents(current);
+ return;
+ }
+ }
+ }
+
+ function commitNestedUnmounts(root: Fiber): void {
+ // While we're inside a removed host node we don't want to call
+ // removeChild on the inner nodes because they're removed by the top
+ // call anyway. We also want to call componentWillUnmount on all
+ // composites before this host node is removed from the tree. Therefore
+ // we do an inner loop while we're still inside the host node.
+ let node: Fiber = root;
+ while (true) {
+ commitUnmount(node);
+ // Visit children because they may contain more composite or host nodes.
+ // Skip portals because commitUnmount() currently visits them recursively.
+ if (node.child !== null && node.tag !== HostPortal) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === root) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === root) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ }
+
+ function detachFiber(current: Fiber) {
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
+ // should clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child. This child
+ // itself will be GC:ed when the parent updates the next time.
+ current.return = null;
+ current.child = null;
+ if (current.alternate) {
+ current.alternate.child = null;
+ current.alternate.return = null;
+ }
+ }
+
+ if (!config.mutation) {
+ return {
+ commitResetTextContent(finishedWork: Fiber) {},
+ commitPlacement(finishedWork: Fiber) {},
+ commitDeletion(current: Fiber) {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current);
+ detachFiber(current);
+ },
+ commitWork(current: Fiber | null, finishedWork: Fiber) {},
+ commitLifeCycles,
+ commitAttachRef,
+ commitDetachRef,
+ };
+ }
+
+ const {
+ commitMount,
+ commitUpdate,
+ resetTextContent,
+ commitTextUpdate,
+ appendChild,
+ appendChildToContainer,
+ insertBefore,
+ insertInContainerBefore,
+ removeChild,
+ removeChildFromContainer,
+ } = config.mutation;
+
function getHostParentFiber(fiber: Fiber): Fiber {
let parent = fiber.return;
while (parent !== null) {
@@ -254,36 +449,6 @@ module.exports = function(
}
}
- function commitNestedUnmounts(root: Fiber): void {
- // While we're inside a removed host node we don't want to call
- // removeChild on the inner nodes because they're removed by the top
- // call anyway. We also want to call componentWillUnmount on all
- // composites before this host node is removed from the tree. Therefore
- // we do an inner loop while we're still inside the host node.
- let node: Fiber = root;
- while (true) {
- commitUnmount(node);
- // Visit children because they may contain more composite or host nodes.
- // Skip portals because commitUnmount() currently visits them recursively.
- if (node.child !== null && node.tag !== HostPortal) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- if (node === root) {
- return;
- }
- while (node.sibling === null) {
- if (node.return === null || node.return === root) {
- return;
- }
- node = node.return;
- }
- node.sibling.return = node.return;
- node = node.sibling;
- }
- }
-
function unmountHostComponents(current): void {
// We only have the top Fiber that was inserted but we need recurse down its
// children to find all the terminal nodes.
@@ -375,53 +540,7 @@ module.exports = function(
// Recursively delete all host nodes from the parent.
// Detach refs and call componentWillUnmount() on the whole subtree.
unmountHostComponents(current);
-
- // Cut off the return pointers to disconnect it from the tree. Ideally, we
- // should clear the child pointer of the parent alternate to let this
- // get GC:ed but we don't know which for sure which parent is the current
- // one so we'll settle for GC:ing the subtree of this child. This child
- // itself will be GC:ed when the parent updates the next time.
- current.return = null;
- current.child = null;
- if (current.alternate) {
- current.alternate.child = null;
- current.alternate.return = null;
- }
- }
-
- // User-originating errors (lifecycles and refs) should not interrupt
- // deletion, so don't let them throw. Host-originating errors should
- // interrupt deletion, so it's okay
- function commitUnmount(current: Fiber): void {
- if (typeof onCommitUnmount === 'function') {
- onCommitUnmount(current);
- }
-
- switch (current.tag) {
- case ClassComponent: {
- safelyDetachRef(current);
- const instance = current.stateNode;
- if (typeof instance.componentWillUnmount === 'function') {
- safelyCallComponentWillUnmount(current, instance);
- }
- return;
- }
- case HostComponent: {
- safelyDetachRef(current);
- return;
- }
- case CoroutineComponent: {
- commitNestedUnmounts(current.stateNode);
- return;
- }
- case HostPortal: {
- // TODO: this is recursive.
- // We are also not using this parent because
- // the portal will get pushed immediately.
- unmountHostComponents(current);
- return;
- }
- }
+ detachFiber(current);
}
function commitWork(current: Fiber | null, finishedWork: Fiber): void {
@@ -488,106 +607,12 @@ module.exports = function(
}
}
- function commitLifeCycles(current: Fiber | null, finishedWork: Fiber): void {
- switch (finishedWork.tag) {
- case ClassComponent: {
- const instance = finishedWork.stateNode;
- if (finishedWork.effectTag & Update) {
- if (current === null) {
- if (__DEV__) {
- startPhaseTimer(finishedWork, 'componentDidMount');
- }
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidMount();
- if (__DEV__) {
- stopPhaseTimer();
- }
- } else {
- const prevProps = current.memoizedProps;
- const prevState = current.memoizedState;
- if (__DEV__) {
- startPhaseTimer(finishedWork, 'componentDidUpdate');
- }
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidUpdate(prevProps, prevState);
- if (__DEV__) {
- stopPhaseTimer();
- }
- }
- }
- if (
- finishedWork.effectTag & Callback &&
- finishedWork.updateQueue !== null
- ) {
- commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
- }
- return;
- }
- case HostRoot: {
- const updateQueue = finishedWork.updateQueue;
- if (updateQueue !== null) {
- const instance = finishedWork.child && finishedWork.child.stateNode;
- commitCallbacks(finishedWork, updateQueue, instance);
- }
- return;
- }
- case HostComponent: {
- const instance: I = finishedWork.stateNode;
-
- // Renderers may schedule work to be done after host components are mounted
- // (eg DOM renderer may schedule auto-focus for inputs and form controls).
- // These effects should only be committed when components are first mounted,
- // aka when there is no current/alternate.
- if (current === null && finishedWork.effectTag & Update) {
- const type = finishedWork.type;
- const props = finishedWork.memoizedProps;
- commitMount(instance, type, props, finishedWork);
- }
-
- return;
- }
- case HostText: {
- // We have no life-cycles associated with text.
- return;
- }
- case HostPortal: {
- // We have no life-cycles associated with portals.
- return;
- }
- default: {
- invariant(
- false,
- 'This unit of work tag should not have side-effects. This error is ' +
- 'likely caused by a bug in React. Please file an issue.',
- );
- }
- }
- }
-
- function commitAttachRef(finishedWork: Fiber) {
- const ref = finishedWork.ref;
- if (ref !== null) {
- const instance = finishedWork.stateNode;
- switch (finishedWork.tag) {
- case HostComponent:
- ref(getPublicInstance(instance));
- break;
- default:
- ref(instance);
- }
- }
- }
-
- function commitDetachRef(current: Fiber) {
- const currentRef = current.ref;
- if (currentRef !== null) {
- currentRef(null);
- }
+ function commitResetTextContent(current: Fiber) {
+ resetTextContent(current.stateNode);
}
return {
+ commitResetTextContent,
commitPlacement,
commitDeletion,
commitWork,
diff --git a/src/renderers/shared/fiber/ReactFiberHydrationContext.js b/src/renderers/shared/fiber/ReactFiberHydrationContext.js
index 879bee732e..c1475017dd 100644
--- a/src/renderers/shared/fiber/ReactFiberHydrationContext.js
+++ b/src/renderers/shared/fiber/ReactFiberHydrationContext.js
@@ -36,42 +36,10 @@ export type HydrationContext = {
module.exports = function(
config: HostConfig,
): HydrationContext {
- const {
- shouldSetTextContent,
- canHydrateInstance,
- canHydrateTextInstance,
- getNextHydratableSibling,
- getFirstHydratableChild,
- hydrateInstance,
- hydrateTextInstance,
- didNotMatchHydratedContainerTextInstance,
- didNotMatchHydratedTextInstance,
- didNotHydrateContainerInstance,
- didNotHydrateInstance,
- // TODO: These are currently unused, see below.
- // didNotFindHydratableContainerInstance,
- // didNotFindHydratableContainerTextInstance,
- didNotFindHydratableInstance,
- didNotFindHydratableTextInstance,
- } = config;
+ const {shouldSetTextContent, hydration} = config;
// If this doesn't have hydration mode.
- if (
- !(canHydrateInstance &&
- canHydrateTextInstance &&
- getNextHydratableSibling &&
- getFirstHydratableChild &&
- hydrateInstance &&
- hydrateTextInstance &&
- didNotMatchHydratedContainerTextInstance &&
- didNotMatchHydratedTextInstance &&
- didNotHydrateContainerInstance &&
- didNotHydrateInstance &&
- // didNotFindHydratableContainerInstance &&
- // didNotFindHydratableContainerTextInstance &&
- didNotFindHydratableInstance &&
- didNotFindHydratableTextInstance)
- ) {
+ if (!hydration) {
return {
enterHydrationState() {
return false;
@@ -98,6 +66,24 @@ module.exports = function(
};
}
+ const {
+ canHydrateInstance,
+ canHydrateTextInstance,
+ getNextHydratableSibling,
+ getFirstHydratableChild,
+ hydrateInstance,
+ hydrateTextInstance,
+ didNotMatchHydratedContainerTextInstance,
+ didNotMatchHydratedTextInstance,
+ didNotHydrateContainerInstance,
+ didNotHydrateInstance,
+ // TODO: These are currently unused, see below.
+ // didNotFindHydratableContainerInstance,
+ // didNotFindHydratableContainerTextInstance,
+ didNotFindHydratableInstance,
+ didNotFindHydratableTextInstance,
+ } = hydration;
+
// The deepest Fiber on the stack involved in a hydration context.
// This may have been an insertion or a hydration.
let hydrationParentFiber: null | Fiber = null;
diff --git a/src/renderers/shared/fiber/ReactFiberReconciler.js b/src/renderers/shared/fiber/ReactFiberReconciler.js
index 8df44b6dc2..697f41ca35 100644
--- a/src/renderers/shared/fiber/ReactFiberReconciler.js
+++ b/src/renderers/shared/fiber/ReactFiberReconciler.js
@@ -77,6 +77,35 @@ export type HostConfig = {
rootContainerInstance: C,
hostContext: CX,
): null | PL,
+
+ shouldSetTextContent(type: T, props: P): boolean,
+ shouldDeprioritizeSubtree(type: T, props: P): boolean,
+
+ createTextInstance(
+ text: string,
+ rootContainerInstance: C,
+ hostContext: CX,
+ internalInstanceHandle: OpaqueHandle,
+ ): TI,
+
+ scheduleDeferredCallback(
+ callback: (deadline: Deadline) => void,
+ ): number | void,
+
+ prepareForCommit(): void,
+ resetAfterCommit(): void,
+
+ now(): number,
+
+ useSyncScheduling?: boolean,
+
+ +hydration?: HydrationHostConfig,
+
+ +mutation?: MutableUpdatesHostConfig,
+ +persistence?: PersistentUpdatesHostConfig,
+};
+
+type MutableUpdatesHostConfig = {
commitUpdate(
instance: I,
updatePayload: PL,
@@ -91,19 +120,8 @@ export type HostConfig = {
newProps: P,
internalInstanceHandle: OpaqueHandle,
): void,
-
- shouldSetTextContent(type: T, props: P): boolean,
- resetTextContent(instance: I): void,
- shouldDeprioritizeSubtree(type: T, props: P): boolean,
-
- createTextInstance(
- text: string,
- rootContainerInstance: C,
- hostContext: CX,
- internalInstanceHandle: OpaqueHandle,
- ): TI,
commitTextUpdate(textInstance: TI, oldText: string, newText: string): void,
-
+ resetTextContent(instance: I): void,
appendChild(parentInstance: I, child: I | TI): void,
appendChildToContainer(container: C, child: I | TI): void,
insertBefore(parentInstance: I, child: I | TI, beforeChild: I | TI): void,
@@ -114,80 +132,92 @@ export type HostConfig = {
): void,
removeChild(parentInstance: I, child: I | TI): void,
removeChildFromContainer(container: C, child: I | TI): void,
+};
- scheduleDeferredCallback(
- callback: (deadline: Deadline) => void,
- ): number | void,
+type PersistentUpdatesHostConfig = {
+ cloneInstance(
+ instance: I,
+ updatePayload: PL,
+ type: T,
+ oldProps: P,
+ newProps: P,
+ internalInstanceHandle: OpaqueHandle,
+ keepChildren: boolean,
+ ): I,
+ tryToReuseInstance(
+ instance: I,
+ updatePayload: PL,
+ type: T,
+ oldProps: P,
+ newProps: P,
+ internalInstanceHandle: OpaqueHandle,
+ keepChildren: boolean,
+ ): I,
- prepareForCommit(): void,
- resetAfterCommit(): void,
-
- now(): number,
+ createRootInstance(rootContainerInstance: C, hostContext: CX): I,
+ commitRootInstance(rootInstance: I): void,
+};
+type HydrationHostConfig = {
// Optional hydration
- canHydrateInstance?: (instance: I | TI, type: T, props: P) => boolean,
- canHydrateTextInstance?: (instance: I | TI, text: string) => boolean,
- getNextHydratableSibling?: (instance: I | TI) => null | I | TI,
- getFirstHydratableChild?: (parentInstance: I | C) => null | I | TI,
- hydrateInstance?: (
+ canHydrateInstance(instance: I | TI, type: T, props: P): boolean,
+ canHydrateTextInstance(instance: I | TI, text: string): boolean,
+ getNextHydratableSibling(instance: I | TI): null | I | TI,
+ getFirstHydratableChild(parentInstance: I | C): null | I | TI,
+ hydrateInstance(
instance: I,
type: T,
props: P,
rootContainerInstance: C,
hostContext: CX,
internalInstanceHandle: OpaqueHandle,
- ) => null | PL,
- hydrateTextInstance?: (
+ ): null | PL,
+ hydrateTextInstance(
textInstance: TI,
text: string,
internalInstanceHandle: OpaqueHandle,
- ) => boolean,
- didNotMatchHydratedContainerTextInstance?: (
+ ): boolean,
+ didNotMatchHydratedContainerTextInstance(
parentContainer: C,
textInstance: TI,
text: string,
- ) => void,
- didNotMatchHydratedTextInstance?: (
+ ): void,
+ didNotMatchHydratedTextInstance(
parentType: T,
parentProps: P,
parentInstance: I,
textInstance: TI,
text: string,
- ) => void,
- didNotHydrateContainerInstance?: (
- parentContainer: C,
- instance: I | TI,
- ) => void,
- didNotHydrateInstance?: (
+ ): void,
+ didNotHydrateContainerInstance(parentContainer: C, instance: I | TI): void,
+ didNotHydrateInstance(
parentType: T,
parentProps: P,
parentInstance: I,
instance: I | TI,
- ) => void,
- didNotFindHydratableContainerInstance?: (
+ ): void,
+ didNotFindHydratableContainerInstance(
parentContainer: C,
type: T,
props: P,
- ) => void,
- didNotFindHydratableContainerTextInstance?: (
+ ): void,
+ didNotFindHydratableContainerTextInstance(
parentContainer: C,
text: string,
- ) => void,
- didNotFindHydratableInstance?: (
+ ): void,
+ didNotFindHydratableInstance(
parentType: T,
parentProps: P,
parentInstance: I,
type: T,
props: P,
- ) => void,
- didNotFindHydratableTextInstance?: (
+ ): void,
+ didNotFindHydratableTextInstance(
parentType: T,
parentProps: P,
parentInstance: I,
text: string,
- ) => void,
-
- useSyncScheduling?: boolean,
+ ): void,
};
export type Reconciler = {
diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js
index bf026e60f4..13bcea3b5b 100644
--- a/src/renderers/shared/fiber/ReactFiberScheduler.js
+++ b/src/renderers/shared/fiber/ReactFiberScheduler.js
@@ -174,6 +174,7 @@ module.exports = function(
hydrationContext,
);
const {
+ commitResetTextContent,
commitPlacement,
commitDeletion,
commitWork,
@@ -332,7 +333,7 @@ module.exports = function(
const effectTag = nextEffect.effectTag;
if (effectTag & ContentReset) {
- config.resetTextContent(nextEffect.stateNode);
+ commitResetTextContent(nextEffect);
}
if (effectTag & Ref) {
diff --git a/src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js b/src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
index 6041663271..a2bb03f609 100644
--- a/src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
+++ b/src/renderers/shared/fiber/__tests__/ReactFiberHostContext-test.js
@@ -43,13 +43,15 @@ describe('ReactFiberHostContext', () => {
appendInitialChild: function() {
return null;
},
- appendChildToContainer: function() {
- return null;
- },
now: function() {
return 0;
},
useSyncScheduling: true,
+ mutation: {
+ appendChildToContainer: function() {
+ return null;
+ },
+ },
});
const container = Renderer.createContainer(/* root: */ null);
diff --git a/src/renderers/testing/ReactTestRendererFiberEntry.js b/src/renderers/testing/ReactTestRendererFiberEntry.js
index 2a6d9e1ea5..1b0253f512 100644
--- a/src/renderers/testing/ReactTestRendererFiberEntry.js
+++ b/src/renderers/testing/ReactTestRendererFiberEntry.js
@@ -180,35 +180,10 @@ var TestRenderer = ReactFiberReconciler({
return UPDATE_SIGNAL;
},
- commitUpdate(
- instance: Instance,
- updatePayload: {},
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- instance.type = type;
- instance.props = newProps;
- },
-
- commitMount(
- instance: Instance,
- type: string,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- // noop
- },
-
shouldSetTextContent(type: string, props: Props): boolean {
return false;
},
- resetTextContent(testElement: Instance): void {
- // noop
- },
-
shouldDeprioritizeSubtree(type: string, props: Props): boolean {
return false;
},
@@ -225,21 +200,6 @@ var TestRenderer = ReactFiberReconciler({
};
},
- commitTextUpdate(
- textInstance: TextInstance,
- oldText: string,
- newText: string,
- ): void {
- textInstance.text = newText;
- },
-
- appendChild: appendChild,
- appendChildToContainer: appendChild,
- insertBefore: insertBefore,
- insertInContainerBefore: insertBefore,
- removeChild: removeChild,
- removeChildFromContainer: removeChild,
-
scheduleDeferredCallback(fn: Function): void {
setTimeout(fn, 0, {timeRemaining: Infinity});
},
@@ -252,6 +212,47 @@ var TestRenderer = ReactFiberReconciler({
// Test renderer does not use expiration
return 0;
},
+
+ mutation: {
+ commitUpdate(
+ instance: Instance,
+ updatePayload: {},
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ instance.type = type;
+ instance.props = newProps;
+ },
+
+ commitMount(
+ instance: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ ): void {
+ // noop
+ },
+
+ commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+ ): void {
+ textInstance.text = newText;
+ },
+ resetTextContent(testElement: Instance): void {
+ // noop
+ },
+
+ appendChild: appendChild,
+ appendChildToContainer: appendChild,
+ insertBefore: insertBefore,
+ insertInContainerBefore: insertBefore,
+ removeChild: removeChild,
+ removeChildFromContainer: removeChild,
+ },
});
var defaultTestOptions = {