diff --git a/packages/react-art/src/ReactART.js b/packages/react-art/src/ReactART.js
index d24395c462268..7ee384707f12e 100644
--- a/packages/react-art/src/ReactART.js
+++ b/packages/react-art/src/ReactART.js
@@ -6,12 +6,11 @@
*/
import React from 'react';
-import ReactFiberReconciler from 'react-reconciler';
+import * as ARTRenderer from 'react-reconciler/inline.art';
import Transform from 'art/core/transform';
import Mode from 'art/modes/current';
import FastNoSideEffects from 'art/modes/fast-noSideEffects';
-import ReactARTHostConfig from './ReactARTHostConfig';
import {TYPES, childrenAsString} from './ReactARTInternals';
Mode.setCurrent(
@@ -132,10 +131,6 @@ class Text extends React.Component {
}
}
-/** ART Renderer */
-
-const ARTRenderer = ReactFiberReconciler(ReactARTHostConfig);
-
/** API */
export const ClippingRectangle = TYPES.CLIPPING_RECTANGLE;
diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js
index 8fa175e1aee50..eae266eb490f6 100644
--- a/packages/react-art/src/ReactARTHostConfig.js
+++ b/packages/react-art/src/ReactARTHostConfig.js
@@ -234,157 +234,167 @@ function applyTextProps(instance, props, prevProps = {}) {
}
}
-const ReactARTHostConfig = {
- appendInitialChild(parentInstance, child) {
- if (typeof child === 'string') {
- // Noop for string children of Text (eg {'foo'}{'bar'})
- invariant(false, 'Text children should already be flattened.');
- return;
- }
+export * from 'shared/HostConfigWithNoPersistence';
+export * from 'shared/HostConfigWithNoHydration';
+
+export function appendInitialChild(parentInstance, child) {
+ if (typeof child === 'string') {
+ // Noop for string children of Text (eg {'foo'}{'bar'})
+ invariant(false, 'Text children should already be flattened.');
+ return;
+ }
- child.inject(parentInstance);
- },
-
- createInstance(type, props, internalInstanceHandle) {
- let instance;
-
- switch (type) {
- case TYPES.CLIPPING_RECTANGLE:
- instance = Mode.ClippingRectangle();
- instance._applyProps = applyClippingRectangleProps;
- break;
- case TYPES.GROUP:
- instance = Mode.Group();
- instance._applyProps = applyGroupProps;
- break;
- case TYPES.SHAPE:
- instance = Mode.Shape();
- instance._applyProps = applyShapeProps;
- break;
- case TYPES.TEXT:
- instance = Mode.Text(
- props.children,
- props.font,
- props.alignment,
- props.path,
- );
- instance._applyProps = applyTextProps;
- break;
- }
+ child.inject(parentInstance);
+}
- invariant(instance, 'ReactART does not support the type "%s"', type);
+export function createInstance(type, props, internalInstanceHandle) {
+ let instance;
+
+ switch (type) {
+ case TYPES.CLIPPING_RECTANGLE:
+ instance = Mode.ClippingRectangle();
+ instance._applyProps = applyClippingRectangleProps;
+ break;
+ case TYPES.GROUP:
+ instance = Mode.Group();
+ instance._applyProps = applyGroupProps;
+ break;
+ case TYPES.SHAPE:
+ instance = Mode.Shape();
+ instance._applyProps = applyShapeProps;
+ break;
+ case TYPES.TEXT:
+ instance = Mode.Text(
+ props.children,
+ props.font,
+ props.alignment,
+ props.path,
+ );
+ instance._applyProps = applyTextProps;
+ break;
+ }
- instance._applyProps(instance, props);
+ invariant(instance, 'ReactART does not support the type "%s"', type);
- return instance;
- },
+ instance._applyProps(instance, props);
- createTextInstance(text, rootContainerInstance, internalInstanceHandle) {
- return text;
- },
+ return instance;
+}
- finalizeInitialChildren(domElement, type, props) {
- return false;
- },
+export function createTextInstance(
+ text,
+ rootContainerInstance,
+ internalInstanceHandle,
+) {
+ return text;
+}
- getPublicInstance(instance) {
- return instance;
- },
+export function finalizeInitialChildren(domElement, type, props) {
+ return false;
+}
- prepareForCommit() {
- // Noop
- },
+export function getPublicInstance(instance) {
+ return instance;
+}
- prepareUpdate(domElement, type, oldProps, newProps) {
- return UPDATE_SIGNAL;
- },
+export function prepareForCommit() {
+ // Noop
+}
- resetAfterCommit() {
- // Noop
- },
+export function prepareUpdate(domElement, type, oldProps, newProps) {
+ return UPDATE_SIGNAL;
+}
- resetTextContent(domElement) {
- // Noop
- },
+export function resetAfterCommit() {
+ // Noop
+}
- shouldDeprioritizeSubtree(type, props) {
- return false;
- },
+export function resetTextContent(domElement) {
+ // Noop
+}
- getRootHostContext() {
- return emptyObject;
- },
+export function shouldDeprioritizeSubtree(type, props) {
+ return false;
+}
- getChildHostContext() {
- return emptyObject;
- },
+export function getRootHostContext() {
+ return emptyObject;
+}
- scheduleDeferredCallback: ReactScheduler.scheduleWork,
+export function getChildHostContext() {
+ return emptyObject;
+}
- shouldSetTextContent(type, props) {
- return (
- typeof props.children === 'string' || typeof props.children === 'number'
- );
- },
-
- now: ReactScheduler.now,
-
- // The ART renderer is secondary to the React DOM renderer.
- isPrimaryRenderer: false,
-
- 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);
- },
+export const scheduleDeferredCallback = ReactScheduler.scheduleWork;
+export const cancelDeferredCallback = ReactScheduler.cancelScheduledWork;
- insertInContainerBefore(parentInstance, child, beforeChild) {
- invariant(
- child !== beforeChild,
- 'ReactART: Can not insert node before itself',
- );
- child.injectBefore(beforeChild);
- },
+export function shouldSetTextContent(type, props) {
+ return (
+ typeof props.children === 'string' || typeof props.children === 'number'
+ );
+}
- removeChild(parentInstance, child) {
- destroyEventListeners(child);
- child.eject();
- },
+export const now = ReactScheduler.now;
- removeChildFromContainer(parentInstance, child) {
- destroyEventListeners(child);
- child.eject();
- },
+// The ART renderer is secondary to the React DOM renderer.
+export const isPrimaryRenderer = false;
- commitTextUpdate(textInstance, oldText, newText) {
- // Noop
- },
+export const supportsMutation = true;
- commitMount(instance, type, newProps) {
- // Noop
- },
+export function appendChild(parentInstance, child) {
+ if (child.parentNode === parentInstance) {
+ child.eject();
+ }
+ child.inject(parentInstance);
+}
- commitUpdate(instance, updatePayload, type, oldProps, newProps) {
- instance._applyProps(instance, newProps, oldProps);
- },
- },
-};
+export function appendChildToContainer(parentInstance, child) {
+ if (child.parentNode === parentInstance) {
+ child.eject();
+ }
+ child.inject(parentInstance);
+}
-export default ReactARTHostConfig;
+export function insertBefore(parentInstance, child, beforeChild) {
+ invariant(
+ child !== beforeChild,
+ 'ReactART: Can not insert node before itself',
+ );
+ child.injectBefore(beforeChild);
+}
+
+export function insertInContainerBefore(parentInstance, child, beforeChild) {
+ invariant(
+ child !== beforeChild,
+ 'ReactART: Can not insert node before itself',
+ );
+ child.injectBefore(beforeChild);
+}
+
+export function removeChild(parentInstance, child) {
+ destroyEventListeners(child);
+ child.eject();
+}
+
+export function removeChildFromContainer(parentInstance, child) {
+ destroyEventListeners(child);
+ child.eject();
+}
+
+export function commitTextUpdate(textInstance, oldText, newText) {
+ // Noop
+}
+
+export function commitMount(instance, type, newProps) {
+ // Noop
+}
+
+export function commitUpdate(
+ instance,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+) {
+ instance._applyProps(instance, newProps, oldProps);
+}
diff --git a/packages/react-art/src/__tests__/ReactART-test.js b/packages/react-art/src/__tests__/ReactART-test.js
index ace737e5260d3..d948e84c5e35d 100644
--- a/packages/react-art/src/__tests__/ReactART-test.js
+++ b/packages/react-art/src/__tests__/ReactART-test.js
@@ -15,22 +15,26 @@ const React = require('react');
const ReactDOM = require('react-dom');
const ReactTestUtils = require('react-dom/test-utils');
-let Group;
-let Shape;
-let Surface;
-let TestComponent;
-
-const Missing = {};
+// Isolate test renderer.
+jest.resetModules();
+const ReactTestRenderer = require('react-test-renderer');
+// Isolate ART renderer.
+jest.resetModules();
const ReactART = require('react-art');
const ARTSVGMode = require('art/modes/svg');
const ARTCurrentMode = require('art/modes/current');
-
-const renderer = require('react-test-renderer');
const Circle = require('react-art/Circle');
const Rectangle = require('react-art/Rectangle');
const Wedge = require('react-art/Wedge');
+let Group;
+let Shape;
+let Surface;
+let TestComponent;
+
+const Missing = {};
+
function testDOMNodeStructure(domNode, expectedStructure) {
expect(domNode).toBeDefined();
expect(domNode.nodeName).toBe(expectedStructure.nodeName);
@@ -362,7 +366,7 @@ describe('ReactART', () => {
// Using test renderer instead of the DOM renderer here because async
// testing APIs for the DOM renderer don't exist.
- const testRenderer = renderer.create(
+ const testRenderer = ReactTestRenderer.create(
@@ -397,7 +401,7 @@ describe('ReactART', () => {
describe('ReactARTComponents', () => {
it('should generate a with props for drawing the Circle', () => {
- const circle = renderer.create(
+ const circle = ReactTestRenderer.create(
,
);
expect(circle.toJSON()).toMatchSnapshot();
@@ -405,7 +409,9 @@ describe('ReactARTComponents', () => {
it('should warn if radius is missing on a Circle component', () => {
expect(() =>
- renderer.create(),
+ ReactTestRenderer.create(
+ ,
+ ),
).toWarnDev(
'Warning: Failed prop type: The prop `radius` is marked as required in `Circle`, ' +
'but its value is `undefined`.' +
@@ -414,7 +420,7 @@ describe('ReactARTComponents', () => {
});
it('should generate a with props for drawing the Rectangle', () => {
- const rectangle = renderer.create(
+ const rectangle = ReactTestRenderer.create(
,
);
expect(rectangle.toJSON()).toMatchSnapshot();
@@ -422,7 +428,7 @@ describe('ReactARTComponents', () => {
it('should warn if width/height is missing on a Rectangle component', () => {
expect(() =>
- renderer.create(),
+ ReactTestRenderer.create(),
).toWarnDev([
'Warning: Failed prop type: The prop `width` is marked as required in `Rectangle`, ' +
'but its value is `undefined`.' +
@@ -434,21 +440,21 @@ describe('ReactARTComponents', () => {
});
it('should generate a with props for drawing the Wedge', () => {
- const wedge = renderer.create(
+ const wedge = ReactTestRenderer.create(
,
);
expect(wedge.toJSON()).toMatchSnapshot();
});
it('should return null if startAngle equals to endAngle on Wedge', () => {
- const wedge = renderer.create(
+ const wedge = ReactTestRenderer.create(
,
);
expect(wedge.toJSON()).toBeNull();
});
it('should warn if outerRadius/startAngle/endAngle is missing on a Wedge component', () => {
- expect(() => renderer.create()).toWarnDev([
+ expect(() => ReactTestRenderer.create()).toWarnDev([
'Warning: Failed prop type: The prop `outerRadius` is marked as required in `Wedge`, ' +
'but its value is `undefined`.' +
'\n in Wedge (at **)',
diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js
index 40450e5f5eef4..4526bcf316d91 100644
--- a/packages/react-dom/src/client/ReactDOM.js
+++ b/packages/react-dom/src/client/ReactDOM.js
@@ -19,7 +19,7 @@ import type {Container} from './ReactDOMHostConfig';
import '../shared/checkReact';
import './ReactDOMClientInjection';
-import ReactFiberReconciler from 'react-reconciler';
+import * as DOMRenderer from 'react-reconciler/inline.dom';
import * as ReactPortal from 'shared/ReactPortal';
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import * as ReactGenericBatching from 'events/ReactGenericBatching';
@@ -35,7 +35,6 @@ import invariant from 'fbjs/lib/invariant';
import lowPriorityWarning from 'shared/lowPriorityWarning';
import warning from 'fbjs/lib/warning';
-import ReactDOMHostConfig from './ReactDOMHostConfig';
import * as ReactDOMComponentTree from './ReactDOMComponentTree';
import * as ReactDOMFiberComponent from './ReactDOMFiberComponent';
import * as ReactDOMEventListener from '../events/ReactDOMEventListener';
@@ -447,8 +446,6 @@ function shouldHydrateDueToLegacyHeuristic(container) {
);
}
-const DOMRenderer = ReactFiberReconciler(ReactDOMHostConfig);
-
ReactGenericBatching.injection.injectRenderer(DOMRenderer);
let warnedAboutHydrateAPI = false;
diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js
index 72072debd5f21..15385db2eec14 100644
--- a/packages/react-dom/src/client/ReactDOMHostConfig.js
+++ b/packages/react-dom/src/client/ReactDOMHostConfig.js
@@ -24,22 +24,26 @@ import {
DOCUMENT_FRAGMENT_NODE,
} from '../shared/HTMLNodeType';
-export type Container = Element | Document;
-type Props = {
+export type Type = string;
+export type Props = {
autoFocus?: boolean,
children?: mixed,
hidden?: boolean,
suppressHydrationWarning?: boolean,
};
-type Instance = Element;
-type TextInstance = Text;
-
+export type Container = Element | Document;
+export type Instance = Element;
+export type TextInstance = Text;
+export type HydratableInstance = Element | Text;
+export type PublicInstance = Element | Text;
type HostContextDev = {
namespace: string,
ancestorInfo: mixed,
};
type HostContextProd = string;
-type HostContext = HostContextDev | HostContextProd;
+export type HostContext = HostContextDev | HostContextProd;
+export type UpdatePayload = Array;
+export type ChildSet = void; // Unused
const {
createElement,
@@ -77,492 +81,500 @@ function shouldAutoFocusHostComponent(type: string, props: Props): boolean {
return false;
}
-const ReactDOMHostConfig = {
- getRootHostContext(rootContainerInstance: Container): HostContext {
- let type;
- let namespace;
- const nodeType = rootContainerInstance.nodeType;
- switch (nodeType) {
- case DOCUMENT_NODE:
- case DOCUMENT_FRAGMENT_NODE: {
- type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
- let root = (rootContainerInstance: any).documentElement;
- namespace = root ? root.namespaceURI : getChildNamespace(null, '');
- break;
- }
- default: {
- const container: any =
- nodeType === COMMENT_NODE
- ? rootContainerInstance.parentNode
- : rootContainerInstance;
- const ownNamespace = container.namespaceURI || null;
- type = container.tagName;
- namespace = getChildNamespace(ownNamespace, type);
- break;
- }
+export * from 'shared/HostConfigWithNoPersistence';
+
+export function getRootHostContext(
+ rootContainerInstance: Container,
+): HostContext {
+ let type;
+ let namespace;
+ const nodeType = rootContainerInstance.nodeType;
+ switch (nodeType) {
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE: {
+ type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
+ let root = (rootContainerInstance: any).documentElement;
+ namespace = root ? root.namespaceURI : getChildNamespace(null, '');
+ break;
}
- if (__DEV__) {
- const validatedTag = type.toLowerCase();
- const ancestorInfo = updatedAncestorInfo(null, validatedTag, null);
- return {namespace, ancestorInfo};
+ default: {
+ const container: any =
+ nodeType === COMMENT_NODE
+ ? rootContainerInstance.parentNode
+ : rootContainerInstance;
+ const ownNamespace = container.namespaceURI || null;
+ type = container.tagName;
+ namespace = getChildNamespace(ownNamespace, type);
+ break;
}
- return namespace;
- },
-
- getChildHostContext(
- parentHostContext: HostContext,
- type: string,
- ): HostContext {
- if (__DEV__) {
- const parentHostContextDev = ((parentHostContext: any): HostContextDev);
- const namespace = getChildNamespace(parentHostContextDev.namespace, type);
- const ancestorInfo = updatedAncestorInfo(
- parentHostContextDev.ancestorInfo,
+ }
+ if (__DEV__) {
+ const validatedTag = type.toLowerCase();
+ const ancestorInfo = updatedAncestorInfo(null, validatedTag, null);
+ return {namespace, ancestorInfo};
+ }
+ return namespace;
+}
+
+export function getChildHostContext(
+ parentHostContext: HostContext,
+ type: string,
+ rootContainerInstance: Container,
+): HostContext {
+ if (__DEV__) {
+ const parentHostContextDev = ((parentHostContext: any): HostContextDev);
+ const namespace = getChildNamespace(parentHostContextDev.namespace, type);
+ const ancestorInfo = updatedAncestorInfo(
+ parentHostContextDev.ancestorInfo,
+ type,
+ null,
+ );
+ return {namespace, ancestorInfo};
+ }
+ const parentNamespace = ((parentHostContext: any): HostContextProd);
+ return getChildNamespace(parentNamespace, type);
+}
+
+export function getPublicInstance(instance: Instance): * {
+ return instance;
+}
+
+export function prepareForCommit(containerInfo: Container): void {
+ eventsEnabled = ReactBrowserEventEmitter.isEnabled();
+ selectionInformation = ReactInputSelection.getSelectionInformation();
+ ReactBrowserEventEmitter.setEnabled(false);
+}
+
+export function resetAfterCommit(containerInfo: Container): void {
+ ReactInputSelection.restoreSelection(selectionInformation);
+ selectionInformation = null;
+ ReactBrowserEventEmitter.setEnabled(eventsEnabled);
+ eventsEnabled = null;
+}
+
+export function createInstance(
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): Instance {
+ let parentNamespace: string;
+ if (__DEV__) {
+ // TODO: take namespace into account when validating.
+ const hostContextDev = ((hostContext: any): HostContextDev);
+ validateDOMNesting(type, null, hostContextDev.ancestorInfo);
+ if (
+ typeof props.children === 'string' ||
+ typeof props.children === 'number'
+ ) {
+ const string = '' + props.children;
+ const ownAncestorInfo = updatedAncestorInfo(
+ hostContextDev.ancestorInfo,
type,
null,
);
- return {namespace, ancestorInfo};
+ validateDOMNesting(null, string, ownAncestorInfo);
}
- const parentNamespace = ((parentHostContext: any): HostContextProd);
- return getChildNamespace(parentNamespace, type);
- },
-
- getPublicInstance(instance: Instance | TextInstance): * {
- return instance;
- },
-
- prepareForCommit(): void {
- eventsEnabled = ReactBrowserEventEmitter.isEnabled();
- selectionInformation = ReactInputSelection.getSelectionInformation();
- ReactBrowserEventEmitter.setEnabled(false);
- },
-
- resetAfterCommit(): void {
- ReactInputSelection.restoreSelection(selectionInformation);
- selectionInformation = null;
- ReactBrowserEventEmitter.setEnabled(eventsEnabled);
- eventsEnabled = null;
- },
-
- createInstance(
- type: string,
- props: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): Instance {
- let parentNamespace: string;
- if (__DEV__) {
- // TODO: take namespace into account when validating.
- const hostContextDev = ((hostContext: any): HostContextDev);
- validateDOMNesting(type, null, hostContextDev.ancestorInfo);
- if (
- typeof props.children === 'string' ||
- typeof props.children === 'number'
- ) {
- const string = '' + props.children;
- const ownAncestorInfo = updatedAncestorInfo(
- hostContextDev.ancestorInfo,
- type,
- null,
- );
- validateDOMNesting(null, string, ownAncestorInfo);
- }
- parentNamespace = hostContextDev.namespace;
- } else {
- parentNamespace = ((hostContext: any): HostContextProd);
+ parentNamespace = hostContextDev.namespace;
+ } else {
+ parentNamespace = ((hostContext: any): HostContextProd);
+ }
+ const domElement: Instance = createElement(
+ type,
+ props,
+ rootContainerInstance,
+ parentNamespace,
+ );
+ precacheFiberNode(internalInstanceHandle, domElement);
+ updateFiberProps(domElement, props);
+ return domElement;
+}
+
+export function appendInitialChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+): void {
+ parentInstance.appendChild(child);
+}
+
+export function finalizeInitialChildren(
+ domElement: Instance,
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): boolean {
+ setInitialProperties(domElement, type, props, rootContainerInstance);
+ return shouldAutoFocusHostComponent(type, props);
+}
+
+export function prepareUpdate(
+ domElement: Instance,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): null | Array {
+ if (__DEV__) {
+ const hostContextDev = ((hostContext: any): HostContextDev);
+ if (
+ typeof newProps.children !== typeof oldProps.children &&
+ (typeof newProps.children === 'string' ||
+ typeof newProps.children === 'number')
+ ) {
+ const string = '' + newProps.children;
+ const ownAncestorInfo = updatedAncestorInfo(
+ hostContextDev.ancestorInfo,
+ type,
+ null,
+ );
+ validateDOMNesting(null, string, ownAncestorInfo);
}
- const domElement: Instance = createElement(
- type,
- props,
- rootContainerInstance,
- parentNamespace,
- );
- precacheFiberNode(internalInstanceHandle, domElement);
- updateFiberProps(domElement, props);
- return domElement;
- },
-
- appendInitialChild(
- parentInstance: Instance,
- child: Instance | TextInstance,
- ): void {
- parentInstance.appendChild(child);
- },
-
- finalizeInitialChildren(
- domElement: Instance,
- type: string,
- props: Props,
- rootContainerInstance: Container,
- ): boolean {
- setInitialProperties(domElement, type, props, rootContainerInstance);
- return shouldAutoFocusHostComponent(type, props);
- },
-
- prepareUpdate(
- domElement: Instance,
- type: string,
- oldProps: Props,
- newProps: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- ): null | Array {
- if (__DEV__) {
- const hostContextDev = ((hostContext: any): HostContextDev);
- if (
- typeof newProps.children !== typeof oldProps.children &&
- (typeof newProps.children === 'string' ||
- typeof newProps.children === 'number')
- ) {
- const string = '' + newProps.children;
- const ownAncestorInfo = updatedAncestorInfo(
- hostContextDev.ancestorInfo,
- type,
- null,
- );
- validateDOMNesting(null, string, ownAncestorInfo);
- }
+ }
+ return diffProperties(
+ domElement,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ );
+}
+
+export function shouldSetTextContent(type: string, props: Props): boolean {
+ return (
+ type === 'textarea' ||
+ typeof props.children === 'string' ||
+ typeof props.children === 'number' ||
+ (typeof props.dangerouslySetInnerHTML === 'object' &&
+ props.dangerouslySetInnerHTML !== null &&
+ typeof props.dangerouslySetInnerHTML.__html === 'string')
+ );
+}
+
+export function shouldDeprioritizeSubtree(type: string, props: Props): boolean {
+ return !!props.hidden;
+}
+
+export function createTextInstance(
+ text: string,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): TextInstance {
+ if (__DEV__) {
+ const hostContextDev = ((hostContext: any): HostContextDev);
+ validateDOMNesting(null, text, hostContextDev.ancestorInfo);
+ }
+ const textNode: TextInstance = createTextNode(text, rootContainerInstance);
+ precacheFiberNode(internalInstanceHandle, textNode);
+ return textNode;
+}
+
+export const now = ReactScheduler.now;
+export const isPrimaryRenderer = true;
+export const scheduleDeferredCallback = ReactScheduler.scheduleWork;
+export const cancelDeferredCallback = ReactScheduler.cancelScheduledWork;
+
+// -------------------
+// Mutation
+// -------------------
+
+export const supportsMutation = true;
+
+export function commitMount(
+ domElement: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+): void {
+ // Despite the naming that might imply otherwise, this method only
+ // fires if there is an `Update` effect scheduled during mounting.
+ // This happens if `finalizeInitialChildren` returns `true` (which it
+ // does to implement the `autoFocus` attribute on the client). But
+ // there are also other cases when this might happen (such as patching
+ // up text content during hydration mismatch). So we'll check this again.
+ if (shouldAutoFocusHostComponent(type, newProps)) {
+ ((domElement: any):
+ | HTMLButtonElement
+ | HTMLInputElement
+ | HTMLSelectElement
+ | HTMLTextAreaElement).focus();
+ }
+}
+
+export function 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);
+}
+
+export function resetTextContent(domElement: Instance): void {
+ setTextContent(domElement, '');
+}
+
+export function commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+): void {
+ textInstance.nodeValue = newText;
+}
+
+export function appendChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+): void {
+ parentInstance.appendChild(child);
+}
+
+export function appendChildToContainer(
+ container: Container,
+ child: Instance | TextInstance,
+): void {
+ if (container.nodeType === COMMENT_NODE) {
+ (container.parentNode: any).insertBefore(child, container);
+ } else {
+ container.appendChild(child);
+ }
+}
+
+export function insertBefore(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+ beforeChild: Instance | TextInstance,
+): void {
+ parentInstance.insertBefore(child, beforeChild);
+}
+
+export function 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);
+ }
+}
+
+export function removeChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+): void {
+ parentInstance.removeChild(child);
+}
+
+export function removeChildFromContainer(
+ container: Container,
+ child: Instance | TextInstance,
+): void {
+ if (container.nodeType === COMMENT_NODE) {
+ (container.parentNode: any).removeChild(child);
+ } else {
+ container.removeChild(child);
+ }
+}
+
+// -------------------
+// Hydration
+// -------------------
+
+export const supportsHydration = true;
+
+export function canHydrateInstance(
+ instance: Instance | TextInstance,
+ type: string,
+ props: Props,
+): null | Instance {
+ if (
+ instance.nodeType !== ELEMENT_NODE ||
+ type.toLowerCase() !== instance.nodeName.toLowerCase()
+ ) {
+ return null;
+ }
+ // This has now been refined to an element node.
+ return ((instance: any): Instance);
+}
+
+export function canHydrateTextInstance(
+ instance: Instance | TextInstance,
+ text: string,
+): null | TextInstance {
+ if (text === '' || instance.nodeType !== TEXT_NODE) {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return null;
+ }
+ // This has now been refined to a text node.
+ return ((instance: any): TextInstance);
+}
+
+export function 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);
+}
+
+export function 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);
+}
+
+export function 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,
+ );
+}
+
+export function hydrateTextInstance(
+ textInstance: TextInstance,
+ text: string,
+ internalInstanceHandle: Object,
+): boolean {
+ precacheFiberNode(internalInstanceHandle, textInstance);
+ return diffHydratedText(textInstance, text);
+}
+
+export function didNotMatchHydratedContainerTextInstance(
+ parentContainer: Container,
+ textInstance: TextInstance,
+ text: string,
+) {
+ if (__DEV__) {
+ warnForUnmatchedText(textInstance, text);
+ }
+}
+
+export function didNotMatchHydratedTextInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ textInstance: TextInstance,
+ text: string,
+) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForUnmatchedText(textInstance, text);
+ }
+}
+
+export function didNotHydrateContainerInstance(
+ parentContainer: Container,
+ instance: Instance | TextInstance,
+) {
+ if (__DEV__) {
+ if (instance.nodeType === 1) {
+ warnForDeletedHydratableElement(parentContainer, (instance: any));
+ } else {
+ warnForDeletedHydratableText(parentContainer, (instance: any));
}
- return diffProperties(
- domElement,
- type,
- oldProps,
- newProps,
- rootContainerInstance,
- );
- },
+ }
+}
- shouldSetTextContent(type: string, props: Props): boolean {
- return (
- type === 'textarea' ||
- typeof props.children === 'string' ||
- typeof props.children === 'number' ||
- (typeof props.dangerouslySetInnerHTML === 'object' &&
- props.dangerouslySetInnerHTML !== null &&
- typeof props.dangerouslySetInnerHTML.__html === 'string')
- );
- },
-
- shouldDeprioritizeSubtree(type: string, props: Props): boolean {
- return !!props.hidden;
- },
-
- createTextInstance(
- text: string,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): TextInstance {
- if (__DEV__) {
- const hostContextDev = ((hostContext: any): HostContextDev);
- validateDOMNesting(null, text, hostContextDev.ancestorInfo);
+export function 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));
}
- const textNode: TextInstance = createTextNode(text, rootContainerInstance);
- precacheFiberNode(internalInstanceHandle, textNode);
- return textNode;
- },
-
- now: ReactScheduler.now,
-
- isPrimaryRenderer: true,
-
- mutation: {
- commitMount(
- domElement: Instance,
- type: string,
- newProps: Props,
- internalInstanceHandle: Object,
- ): void {
- // Despite the naming that might imply otherwise, this method only
- // fires if there is an `Update` effect scheduled during mounting.
- // This happens if `finalizeInitialChildren` returns `true` (which it
- // does to implement the `autoFocus` attribute on the client). But
- // there are also other cases when this might happen (such as patching
- // up text content during hydration mismatch). So we'll check this again.
- if (shouldAutoFocusHostComponent(type, newProps)) {
- ((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);
- },
-
- resetTextContent(domElement: Instance): void {
- setTextContent(domElement, '');
- },
-
- 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);
- }
- },
- },
-
- hydration: {
- canHydrateInstance(
- instance: Instance | TextInstance,
- type: string,
- props: Props,
- ): null | Instance {
- if (
- instance.nodeType !== ELEMENT_NODE ||
- type.toLowerCase() !== instance.nodeName.toLowerCase()
- ) {
- return null;
- }
- // This has now been refined to an element node.
- return ((instance: any): Instance);
- },
-
- canHydrateTextInstance(
- instance: Instance | TextInstance,
- text: string,
- ): null | TextInstance {
- if (text === '' || instance.nodeType !== TEXT_NODE) {
- // Empty strings are not parsed by HTML so there won't be a correct match here.
- return null;
- }
- // This has now been refined to a text node.
- return ((instance: any): TextInstance);
- },
-
- 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);
- },
-
- 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: ReactScheduler.scheduleWork,
- cancelDeferredCallback: ReactScheduler.cancelScheduledWork,
-};
+ }
+}
-export default ReactDOMHostConfig;
+export function didNotFindHydratableContainerInstance(
+ parentContainer: Container,
+ type: string,
+ props: Props,
+) {
+ if (__DEV__) {
+ warnForInsertedHydratedElement(parentContainer, type, props);
+ }
+}
+
+export function didNotFindHydratableContainerTextInstance(
+ parentContainer: Container,
+ text: string,
+) {
+ if (__DEV__) {
+ warnForInsertedHydratedText(parentContainer, text);
+ }
+}
+
+export function didNotFindHydratableInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ type: string,
+ props: Props,
+) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedElement(parentInstance, type, props);
+ }
+}
+
+export function didNotFindHydratableTextInstance(
+ parentType: string,
+ parentProps: Props,
+ parentInstance: Instance,
+ text: string,
+) {
+ if (__DEV__ && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedText(parentInstance, text);
+ }
+}
diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js
index 130b1995800c8..757212fe2000f 100644
--- a/packages/react-native-renderer/src/ReactFabric.js
+++ b/packages/react-native-renderer/src/ReactFabric.js
@@ -12,6 +12,8 @@ import type {ReactNodeList} from 'shared/ReactTypes';
import './ReactFabricInjection';
+import * as ReactFabricRenderer from 'react-reconciler/inline.fabric';
+
import * as ReactPortal from 'shared/ReactPortal';
import * as ReactGenericBatching from 'events/ReactGenericBatching';
import ReactVersion from 'shared/ReactVersion';
@@ -19,7 +21,6 @@ import ReactVersion from 'shared/ReactVersion';
import NativeMethodsMixin from './NativeMethodsMixin';
import ReactNativeComponent from './ReactNativeComponent';
import * as ReactNativeComponentTree from './ReactNativeComponentTree';
-import ReactFabricRenderer from './ReactFabricRenderer';
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
@@ -64,7 +65,7 @@ function findNodeHandle(componentOrHandle: any): ?number {
}
if (hostInstance.canonical) {
// Fabric
- return hostInstance.canonical._nativeTag;
+ return (hostInstance.canonical: any)._nativeTag;
}
return hostInstance._nativeTag;
}
diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js
index 7601062f54f54..321b86753e8f8 100644
--- a/packages/react-native-renderer/src/ReactFabricHostConfig.js
+++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js
@@ -33,9 +33,9 @@ import {
cloneNodeWithNewChildren,
cloneNodeWithNewChildrenAndProps,
cloneNodeWithNewProps,
- createChildSet,
- appendChild,
- appendChildToSet,
+ createChildSet as createChildNodeSet,
+ appendChild as appendChildNode,
+ appendChildToSet as appendChildNodeToSet,
completeRoot,
registerEventHandler,
} from 'FabricUIManager';
@@ -47,9 +47,24 @@ import UIManager from 'UIManager';
// This means that they never overlap.
let nextReactTag = 2;
-type HostContext = $ReadOnly<{|
+type Node = Object;
+export type Type = string;
+export type Props = Object;
+export type Instance = {
+ node: Node,
+ canonical: ReactFabricHostComponent,
+};
+export type TextInstance = {
+ node: Node,
+};
+export type HydratableInstance = Instance | TextInstance;
+export type PublicInstance = ReactFabricHostComponent;
+export type Container = number;
+export type ChildSet = Object;
+export type HostContext = $ReadOnly<{|
isInAParentText: boolean,
|}>;
+export type UpdatePayload = Object;
// TODO: Remove this conditional once all changes have propagated.
if (registerEventHandler) {
@@ -135,247 +150,242 @@ class ReactFabricHostComponent {
// eslint-disable-next-line no-unused-expressions
(ReactFabricHostComponent.prototype: NativeMethodsMixinType);
-type Node = Object;
-type ChildSet = Object;
-type Container = number;
-type Instance = {
- node: Node,
- canonical: ReactFabricHostComponent,
-};
-type Props = Object;
-type TextInstance = {
- node: Node,
-};
-
-const ReactFabricHostConfig = {
- appendInitialChild(
- parentInstance: Instance,
- child: Instance | TextInstance,
- ): void {
- appendChild(parentInstance.node, child.node);
- },
-
- createInstance(
- type: string,
- props: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): Instance {
- const tag = nextReactTag;
- nextReactTag += 2;
+export * from 'shared/HostConfigWithNoMutation';
+export * from 'shared/HostConfigWithNoHydration';
- const viewConfig = ReactNativeViewConfigRegistry.get(type);
+export function appendInitialChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+): void {
+ appendChildNode(parentInstance.node, child.node);
+}
- if (__DEV__) {
- for (const key in viewConfig.validAttributes) {
- if (props.hasOwnProperty(key)) {
- deepFreezeAndThrowOnMutationInDev(props[key]);
- }
+export function createInstance(
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): Instance {
+ const tag = nextReactTag;
+ nextReactTag += 2;
+
+ const viewConfig = ReactNativeViewConfigRegistry.get(type);
+
+ if (__DEV__) {
+ for (const key in viewConfig.validAttributes) {
+ if (props.hasOwnProperty(key)) {
+ deepFreezeAndThrowOnMutationInDev(props[key]);
}
}
+ }
- invariant(
- type !== 'RCTView' || !hostContext.isInAParentText,
- 'Nesting of within is not currently supported.',
- );
+ invariant(
+ type !== 'RCTView' || !hostContext.isInAParentText,
+ 'Nesting of within is not currently supported.',
+ );
+
+ const updatePayload = ReactNativeAttributePayload.create(
+ props,
+ viewConfig.validAttributes,
+ );
+
+ const node = createNode(
+ tag, // reactTag
+ viewConfig.uiViewClassName, // viewName
+ rootContainerInstance, // rootTag
+ updatePayload, // props
+ internalInstanceHandle, // internalInstanceHandle
+ );
+
+ const component = new ReactFabricHostComponent(tag, viewConfig, props);
+
+ return {
+ node: node,
+ canonical: component,
+ };
+}
- const updatePayload = ReactNativeAttributePayload.create(
- props,
- viewConfig.validAttributes,
- );
+export function createTextInstance(
+ text: string,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): TextInstance {
+ invariant(
+ hostContext.isInAParentText,
+ 'Text strings must be rendered within a component.',
+ );
+
+ const tag = nextReactTag;
+ nextReactTag += 2;
+
+ const node = createNode(
+ tag, // reactTag
+ 'RCTRawText', // viewName
+ rootContainerInstance, // rootTag
+ {text: text}, // props
+ internalInstanceHandle, // instance handle
+ );
+
+ return {
+ node: node,
+ };
+}
- const node = createNode(
- tag, // reactTag
- viewConfig.uiViewClassName, // viewName
- rootContainerInstance, // rootTag
- updatePayload, // props
- internalInstanceHandle, // internalInstanceHandle
- );
+export function finalizeInitialChildren(
+ parentInstance: Instance,
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): boolean {
+ return false;
+}
- const component = new ReactFabricHostComponent(tag, viewConfig, props);
-
- return {
- node: node,
- canonical: component,
- };
- },
-
- createTextInstance(
- text: string,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): TextInstance {
- invariant(
- hostContext.isInAParentText,
- 'Text strings must be rendered within a component.',
- );
+export function getRootHostContext(
+ rootContainerInstance: Container,
+): HostContext {
+ return {isInAParentText: false};
+}
+
+export function getChildHostContext(
+ parentHostContext: HostContext,
+ type: string,
+ rootContainerInstance: Container,
+): HostContext {
+ const prevIsInAParentText = parentHostContext.isInAParentText;
+ const isInAParentText =
+ type === 'AndroidTextInput' || // Android
+ type === 'RCTMultilineTextInputView' || // iOS
+ type === 'RCTSinglelineTextInputView' || // iOS
+ type === 'RCTText' ||
+ type === 'RCTVirtualText';
+
+ if (prevIsInAParentText !== isInAParentText) {
+ return {isInAParentText};
+ } else {
+ return parentHostContext;
+ }
+}
- const tag = nextReactTag;
- nextReactTag += 2;
+export function getPublicInstance(instance: Instance): * {
+ return instance.canonical;
+}
- const node = createNode(
- tag, // reactTag
- 'RCTRawText', // viewName
- rootContainerInstance, // rootTag
- {text: text}, // props
- internalInstanceHandle, // instance handle
- );
+export function prepareForCommit(containerInfo: Container): void {
+ // Noop
+}
- return {
- node: node,
- };
- },
+export function prepareUpdate(
+ instance: Instance,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): null | Object {
+ const viewConfig = instance.canonical.viewConfig;
+ const updatePayload = ReactNativeAttributePayload.diff(
+ oldProps,
+ newProps,
+ viewConfig.validAttributes,
+ );
+ // TODO: If the event handlers have changed, we need to update the current props
+ // in the commit phase but there is no host config hook to do it yet.
+ return updatePayload;
+}
- finalizeInitialChildren(
- parentInstance: Instance,
- type: string,
- props: Props,
- rootContainerInstance: Container,
- ): boolean {
- return false;
- },
-
- getRootHostContext(rootContainerInstance: Container): HostContext {
- return {isInAParentText: false};
- },
-
- getChildHostContext(
- parentHostContext: HostContext,
- type: string,
- ): HostContext {
- const prevIsInAParentText = parentHostContext.isInAParentText;
- const isInAParentText =
- type === 'AndroidTextInput' || // Android
- type === 'RCTMultilineTextInputView' || // iOS
- type === 'RCTSinglelineTextInputView' || // iOS
- type === 'RCTText' ||
- type === 'RCTVirtualText';
-
- if (prevIsInAParentText !== isInAParentText) {
- return {isInAParentText};
+export function resetAfterCommit(containerInfo: Container): void {
+ // Noop
+}
+
+export function shouldDeprioritizeSubtree(type: string, props: Props): boolean {
+ return false;
+}
+
+export function shouldSetTextContent(type: string, props: Props): boolean {
+ // TODO (bvaughn) Revisit this decision.
+ // Always returning false simplifies the createInstance() implementation,
+ // But creates an additional child Fiber for raw text children.
+ // No additional native views are created though.
+ // It's not clear to me which is better so I'm deferring for now.
+ // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
+ return false;
+}
+
+// The Fabric renderer is secondary to the existing React Native renderer.
+export const isPrimaryRenderer = false;
+export const now = ReactNativeFrameScheduling.now;
+export const scheduleDeferredCallback =
+ ReactNativeFrameScheduling.scheduleDeferredCallback;
+export const cancelDeferredCallback =
+ ReactNativeFrameScheduling.cancelDeferredCallback;
+
+// -------------------
+// Persistence
+// -------------------
+
+export const supportsPersistence = true;
+
+export function cloneInstance(
+ instance: Instance,
+ updatePayload: null | Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ keepChildren: boolean,
+ recyclableInstance: null | Instance,
+): Instance {
+ const node = instance.node;
+ let clone;
+ if (keepChildren) {
+ if (updatePayload !== null) {
+ clone = cloneNodeWithNewProps(
+ node,
+ updatePayload,
+ internalInstanceHandle,
+ );
} else {
- return parentHostContext;
+ clone = cloneNode(node, internalInstanceHandle);
}
- },
-
- getPublicInstance(instance: Instance): * {
- return instance.canonical;
- },
-
- now: ReactNativeFrameScheduling.now,
-
- // The Fabric renderer is secondary to the existing React Native renderer.
- isPrimaryRenderer: false,
-
- prepareForCommit(): void {
- // Noop
- },
-
- prepareUpdate(
- instance: Instance,
- type: string,
- oldProps: Props,
- newProps: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- ): null | Object {
- const viewConfig = instance.canonical.viewConfig;
- const updatePayload = ReactNativeAttributePayload.diff(
- oldProps,
- newProps,
- viewConfig.validAttributes,
- );
- // TODO: If the event handlers have changed, we need to update the current props
- // in the commit phase but there is no host config hook to do it yet.
- return updatePayload;
- },
-
- resetAfterCommit(): void {
- // Noop
- },
-
- scheduleDeferredCallback: ReactNativeFrameScheduling.scheduleDeferredCallback,
- cancelDeferredCallback: ReactNativeFrameScheduling.cancelDeferredCallback,
-
- shouldDeprioritizeSubtree(type: string, props: Props): boolean {
- return false;
- },
-
- shouldSetTextContent(type: string, props: Props): boolean {
- // TODO (bvaughn) Revisit this decision.
- // Always returning false simplifies the createInstance() implementation,
- // But creates an additional child Fiber for raw text children.
- // No additional native views are created though.
- // It's not clear to me which is better so I'm deferring for now.
- // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
- return false;
- },
-
- persistence: {
- cloneInstance(
- instance: Instance,
- updatePayload: null | Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- keepChildren: boolean,
- recyclableInstance: null | Instance,
- ): Instance {
- const node = instance.node;
- let clone;
- if (keepChildren) {
- if (updatePayload !== null) {
- clone = cloneNodeWithNewProps(
- node,
- updatePayload,
- internalInstanceHandle,
- );
- } else {
- clone = cloneNode(node, internalInstanceHandle);
- }
- } else {
- if (updatePayload !== null) {
- clone = cloneNodeWithNewChildrenAndProps(
- node,
- updatePayload,
- internalInstanceHandle,
- );
- } else {
- clone = cloneNodeWithNewChildren(node, internalInstanceHandle);
- }
- }
- return {
- node: clone,
- canonical: instance.canonical,
- };
- },
-
- createContainerChildSet(container: Container): ChildSet {
- return createChildSet(container);
- },
-
- appendChildToContainerChildSet(
- childSet: ChildSet,
- child: Instance | TextInstance,
- ): void {
- appendChildToSet(childSet, child.node);
- },
-
- finalizeContainerChildren(
- container: Container,
- newChildren: ChildSet,
- ): void {
- completeRoot(container, newChildren);
- },
-
- replaceContainerChildren(
- container: Container,
- newChildren: ChildSet,
- ): void {},
- },
-};
+ } else {
+ if (updatePayload !== null) {
+ clone = cloneNodeWithNewChildrenAndProps(
+ node,
+ updatePayload,
+ internalInstanceHandle,
+ );
+ } else {
+ clone = cloneNodeWithNewChildren(node, internalInstanceHandle);
+ }
+ }
+ return {
+ node: clone,
+ canonical: instance.canonical,
+ };
+}
+
+export function createContainerChildSet(container: Container): ChildSet {
+ return createChildNodeSet(container);
+}
+
+export function appendChildToContainerChildSet(
+ childSet: ChildSet,
+ child: Instance | TextInstance,
+): void {
+ appendChildNodeToSet(childSet, child.node);
+}
+
+export function finalizeContainerChildren(
+ container: Container,
+ newChildren: ChildSet,
+): void {
+ completeRoot(container, newChildren);
+}
-export default ReactFabricHostConfig;
+export function replaceContainerChildren(
+ container: Container,
+ newChildren: ChildSet,
+): void {}
diff --git a/packages/react-native-renderer/src/ReactFabricRenderer.js b/packages/react-native-renderer/src/ReactFabricRenderer.js
deleted file mode 100644
index 8f77d283c9a2e..0000000000000
--- a/packages/react-native-renderer/src/ReactFabricRenderer.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * 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.
- *
- * @flow
- */
-
-import ReactFiberReconciler from 'react-reconciler';
-import ReactFabricHostConfig from './ReactFabricHostConfig';
-
-const ReactFabricRenderer = ReactFiberReconciler(ReactFabricHostConfig);
-
-export default ReactFabricRenderer;
diff --git a/packages/react-native-renderer/src/ReactNativeFrameScheduling.js b/packages/react-native-renderer/src/ReactNativeFrameScheduling.js
index 3f7e7e8818d58..ee88fcf5fa20e 100644
--- a/packages/react-native-renderer/src/ReactNativeFrameScheduling.js
+++ b/packages/react-native-renderer/src/ReactNativeFrameScheduling.js
@@ -7,7 +7,7 @@
* @flow
*/
-import type {Deadline} from 'react-reconciler';
+import type {Deadline} from 'react-reconciler/src/ReactFiberScheduler';
const hasNativePerformanceNow =
typeof performance === 'object' && typeof performance.now === 'function';
@@ -43,7 +43,10 @@ function setTimeoutCallback() {
// RN has a poor polyfill for requestIdleCallback so we aren't using it.
// This implementation is only intended for short-term use anyway.
// We also don't implement cancel functionality b'c Fiber doesn't currently need it.
-function scheduleDeferredCallback(callback: Callback): number {
+function scheduleDeferredCallback(
+ callback: Callback,
+ options?: {timeout: number},
+): number {
// We assume only one callback is scheduled at a time b'c that's how Fiber works.
scheduledCallback = callback;
return setTimeout(setTimeoutCallback, 1);
diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js
index b766d1e6554f7..dcfeb8d90ff44 100644
--- a/packages/react-native-renderer/src/ReactNativeHostConfig.js
+++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js
@@ -26,18 +26,22 @@ import {
import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling';
-type Container = number;
+export type Type = string;
+export type Props = Object;
+export type Container = number;
export type Instance = {
_children: Array,
_nativeTag: number,
viewConfig: ReactNativeBaseComponentViewConfig,
};
-type Props = Object;
-type TextInstance = number;
-
-type HostContext = $ReadOnly<{|
+export type TextInstance = number;
+export type HydratableInstance = Instance | TextInstance;
+export type PublicInstance = Instance;
+export type HostContext = $ReadOnly<{|
isInAParentText: boolean,
|}>;
+export type UpdatePayload = Object; // Unused
+export type ChildSet = void; // Unused
// Counter for uniquely identifying views.
// % 10 === 1 means it is a rootTag.
@@ -63,369 +67,376 @@ function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
}
}
-const ReactNativeHostConfig = {
- appendInitialChild(
- parentInstance: Instance,
- child: Instance | TextInstance,
- ): void {
- parentInstance._children.push(child);
- },
-
- createInstance(
- type: string,
- props: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): Instance {
- const tag = allocateTag();
- const viewConfig = ReactNativeViewConfigRegistry.get(type);
-
- if (__DEV__) {
- for (const key in viewConfig.validAttributes) {
- if (props.hasOwnProperty(key)) {
- deepFreezeAndThrowOnMutationInDev(props[key]);
- }
+export * from 'shared/HostConfigWithNoPersistence';
+export * from 'shared/HostConfigWithNoHydration';
+
+export function appendInitialChild(
+ parentInstance: Instance,
+ child: Instance | TextInstance,
+): void {
+ parentInstance._children.push(child);
+}
+
+export function createInstance(
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): Instance {
+ const tag = allocateTag();
+ const viewConfig = ReactNativeViewConfigRegistry.get(type);
+
+ if (__DEV__) {
+ for (const key in viewConfig.validAttributes) {
+ if (props.hasOwnProperty(key)) {
+ deepFreezeAndThrowOnMutationInDev(props[key]);
}
}
+ }
- invariant(
- type !== 'RCTView' || !hostContext.isInAParentText,
- 'Nesting of within is not currently supported.',
- );
+ invariant(
+ type !== 'RCTView' || !hostContext.isInAParentText,
+ 'Nesting of within is not currently supported.',
+ );
- const updatePayload = ReactNativeAttributePayload.create(
- props,
- viewConfig.validAttributes,
- );
+ const updatePayload = ReactNativeAttributePayload.create(
+ props,
+ viewConfig.validAttributes,
+ );
- UIManager.createView(
- tag, // reactTag
- viewConfig.uiViewClassName, // viewName
- rootContainerInstance, // rootTag
- updatePayload, // props
- );
+ UIManager.createView(
+ tag, // reactTag
+ viewConfig.uiViewClassName, // viewName
+ rootContainerInstance, // rootTag
+ updatePayload, // props
+ );
- const component = new ReactNativeFiberHostComponent(tag, viewConfig);
-
- precacheFiberNode(internalInstanceHandle, tag);
- updateFiberProps(tag, props);
-
- // Not sure how to avoid this cast. Flow is okay if the component is defined
- // in the same file but if it's external it can't see the types.
- return ((component: any): Instance);
- },
-
- createTextInstance(
- text: string,
- rootContainerInstance: Container,
- hostContext: HostContext,
- internalInstanceHandle: Object,
- ): TextInstance {
- invariant(
- hostContext.isInAParentText,
- 'Text strings must be rendered within a component.',
- );
+ const component = new ReactNativeFiberHostComponent(tag, viewConfig);
- const tag = allocateTag();
+ precacheFiberNode(internalInstanceHandle, tag);
+ updateFiberProps(tag, props);
- UIManager.createView(
- tag, // reactTag
- 'RCTRawText', // viewName
- rootContainerInstance, // rootTag
- {text: text}, // props
- );
+ // Not sure how to avoid this cast. Flow is okay if the component is defined
+ // in the same file but if it's external it can't see the types.
+ return ((component: any): Instance);
+}
- precacheFiberNode(internalInstanceHandle, tag);
+export function createTextInstance(
+ text: string,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+ internalInstanceHandle: Object,
+): TextInstance {
+ invariant(
+ hostContext.isInAParentText,
+ 'Text strings must be rendered within a component.',
+ );
- return tag;
- },
+ const tag = allocateTag();
- finalizeInitialChildren(
- parentInstance: Instance,
- type: string,
- props: Props,
- rootContainerInstance: Container,
- ): boolean {
- // Don't send a no-op message over the bridge.
- if (parentInstance._children.length === 0) {
- return false;
- }
+ UIManager.createView(
+ tag, // reactTag
+ 'RCTRawText', // viewName
+ rootContainerInstance, // rootTag
+ {text: text}, // props
+ );
- // Map from child objects to native tags.
- // Either way we need to pass a copy of the Array to prevent it from being frozen.
- const nativeTags = parentInstance._children.map(
- child =>
- typeof child === 'number'
- ? child // Leaf node (eg text)
- : child._nativeTag,
- );
+ precacheFiberNode(internalInstanceHandle, tag);
+
+ return tag;
+}
+
+export function finalizeInitialChildren(
+ parentInstance: Instance,
+ type: string,
+ props: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): boolean {
+ // Don't send a no-op message over the bridge.
+ if (parentInstance._children.length === 0) {
+ return false;
+ }
+
+ // Map from child objects to native tags.
+ // Either way we need to pass a copy of the Array to prevent it from being frozen.
+ const nativeTags = parentInstance._children.map(
+ child =>
+ typeof child === 'number'
+ ? child // Leaf node (eg text)
+ : child._nativeTag,
+ );
+
+ UIManager.setChildren(
+ parentInstance._nativeTag, // containerTag
+ nativeTags, // reactTags
+ );
+
+ return false;
+}
+
+export function getRootHostContext(
+ rootContainerInstance: Container,
+): HostContext {
+ return {isInAParentText: false};
+}
+
+export function getChildHostContext(
+ parentHostContext: HostContext,
+ type: string,
+ rootContainerInstance: Container,
+): HostContext {
+ const prevIsInAParentText = parentHostContext.isInAParentText;
+ const isInAParentText =
+ type === 'AndroidTextInput' || // Android
+ type === 'RCTMultilineTextInputView' || // iOS
+ type === 'RCTSinglelineTextInputView' || // iOS
+ type === 'RCTText' ||
+ type === 'RCTVirtualText';
+
+ if (prevIsInAParentText !== isInAParentText) {
+ return {isInAParentText};
+ } else {
+ return parentHostContext;
+ }
+}
+
+export function getPublicInstance(instance: Instance): * {
+ return instance;
+}
+
+export function prepareForCommit(containerInfo: Container): void {
+ // Noop
+}
+
+export function prepareUpdate(
+ instance: Instance,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ hostContext: HostContext,
+): null | Object {
+ return emptyObject;
+}
+
+export function resetAfterCommit(containerInfo: Container): void {
+ // Noop
+}
+
+export const now = ReactNativeFrameScheduling.now;
+export const isPrimaryRenderer = true;
+export const scheduleDeferredCallback =
+ ReactNativeFrameScheduling.scheduleDeferredCallback;
+export const cancelDeferredCallback =
+ ReactNativeFrameScheduling.cancelDeferredCallback;
- UIManager.setChildren(
+export function shouldDeprioritizeSubtree(type: string, props: Props): boolean {
+ return false;
+}
+
+export function shouldSetTextContent(type: string, props: Props): boolean {
+ // TODO (bvaughn) Revisit this decision.
+ // Always returning false simplifies the createInstance() implementation,
+ // But creates an additional child Fiber for raw text children.
+ // No additional native views are created though.
+ // It's not clear to me which is better so I'm deferring for now.
+ // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
+ return false;
+}
+
+// -------------------
+// Mutation
+// -------------------
+
+export const supportsMutation = true;
+
+export function 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
- nativeTags, // reactTags
+ [index], // moveFromIndices
+ [children.length - 1], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [], // removeAtIndices
);
+ } else {
+ children.push(child);
- return false;
- },
-
- getRootHostContext(rootContainerInstance: Container): HostContext {
- return {isInAParentText: false};
- },
-
- getChildHostContext(
- parentHostContext: HostContext,
- type: string,
- ): HostContext {
- const prevIsInAParentText = parentHostContext.isInAParentText;
- const isInAParentText =
- type === 'AndroidTextInput' || // Android
- type === 'RCTMultilineTextInputView' || // iOS
- type === 'RCTSinglelineTextInputView' || // iOS
- type === 'RCTText' ||
- type === 'RCTVirtualText';
-
- if (prevIsInAParentText !== isInAParentText) {
- return {isInAParentText};
- } else {
- return parentHostContext;
- }
- },
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerTag
+ [], // moveFromIndices
+ [], // moveToIndices
+ [childTag], // addChildReactTags
+ [children.length - 1], // addAtIndices
+ [], // removeAtIndices
+ );
+ }
+}
- getPublicInstance(instance: Instance): * {
- return instance;
- },
+export function appendChildToContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+): void {
+ const childTag = typeof child === 'number' ? child : child._nativeTag;
+ UIManager.setChildren(
+ parentInstance, // containerTag
+ [childTag], // reactTags
+ );
+}
- now: ReactNativeFrameScheduling.now,
+export function commitTextUpdate(
+ textInstance: TextInstance,
+ oldText: string,
+ newText: string,
+): void {
+ UIManager.updateView(
+ textInstance, // reactTag
+ 'RCTRawText', // viewName
+ {text: newText}, // props
+ );
+}
- isPrimaryRenderer: true,
+export function commitMount(
+ instance: Instance,
+ type: string,
+ newProps: Props,
+ internalInstanceHandle: Object,
+): void {
+ // Noop
+}
- prepareForCommit(): void {
- // Noop
- },
+export function 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
+ );
+ }
+}
- prepareUpdate(
- instance: Instance,
- type: string,
- oldProps: Props,
- newProps: Props,
- rootContainerInstance: Container,
- hostContext: HostContext,
- ): null | Object {
- return emptyObject;
- },
+export function 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
+ );
+ }
+}
- resetAfterCommit(): void {
- // Noop
- },
+export function 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',
+ );
+}
- scheduleDeferredCallback: ReactNativeFrameScheduling.scheduleDeferredCallback,
- cancelDeferredCallback: ReactNativeFrameScheduling.cancelDeferredCallback,
+export function 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
+ );
+}
- shouldDeprioritizeSubtree(type: string, props: Props): boolean {
- return false;
- },
-
- shouldSetTextContent(type: string, props: Props): boolean {
- // TODO (bvaughn) Revisit this decision.
- // Always returning false simplifies the createInstance() implementation,
- // But creates an additional child Fiber for raw text children.
- // No additional native views are created though.
- // It's not clear to me which is better so I'm deferring for now.
- // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
- return false;
- },
-
- 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
- },
- },
-};
+export function removeChildFromContainer(
+ parentInstance: Container,
+ child: Instance | TextInstance,
+): void {
+ recursivelyUncacheFiberNode(child);
+ UIManager.manageChildren(
+ parentInstance, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [0], // removeAtIndices
+ );
+}
-export default ReactNativeHostConfig;
+export function resetTextContent(instance: Instance): void {
+ // Noop
+}
diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js
index 7b5f3c7805cce..a66548fdc9023 100644
--- a/packages/react-native-renderer/src/ReactNativeRenderer.js
+++ b/packages/react-native-renderer/src/ReactNativeRenderer.js
@@ -12,7 +12,7 @@ import type {ReactNodeList} from 'shared/ReactTypes';
import './ReactNativeInjection';
-import ReactFiberReconciler from 'react-reconciler';
+import * as ReactNativeFiberRenderer from 'react-reconciler/inline.native';
import * as ReactPortal from 'shared/ReactPortal';
import * as ReactGenericBatching from 'events/ReactGenericBatching';
import ReactVersion from 'shared/ReactVersion';
@@ -21,7 +21,6 @@ import UIManager from 'UIManager';
import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
-import ReactNativeHostConfig from './ReactNativeHostConfig';
import NativeMethodsMixin from './NativeMethodsMixin';
import ReactNativeComponent from './ReactNativeComponent';
import * as ReactNativeComponentTree from './ReactNativeComponentTree';
@@ -31,8 +30,6 @@ import {ReactCurrentOwner} from 'shared/ReactGlobalSharedState';
import getComponentName from 'shared/getComponentName';
import warning from 'fbjs/lib/warning';
-const ReactNativeFiberRenderer = ReactFiberReconciler(ReactNativeHostConfig);
-
const findHostInstance = ReactNativeFiberRenderer.findHostInstance;
function findNodeHandle(componentOrHandle: any): ?number {
@@ -71,7 +68,7 @@ function findNodeHandle(componentOrHandle: any): ?number {
}
if (hostInstance.canonical) {
// Fabric
- return hostInstance.canonical._nativeTag;
+ return (hostInstance.canonical: any)._nativeTag;
}
return hostInstance._nativeTag;
}
diff --git a/packages/react-noop-renderer/src/ReactNoop.js b/packages/react-noop-renderer/src/ReactNoop.js
index 91ae6ca74994e..765a6887bceec 100644
--- a/packages/react-noop-renderer/src/ReactNoop.js
+++ b/packages/react-noop-renderer/src/ReactNoop.js
@@ -14,9 +14,11 @@
* environment.
*/
+import ReactFiberReconciler from 'react-reconciler';
import createReactNoop from './createReactNoop';
const ReactNoop = createReactNoop(
+ ReactFiberReconciler, // reconciler
true, // useMutation
);
diff --git a/packages/react-noop-renderer/src/ReactNoopPersistent.js b/packages/react-noop-renderer/src/ReactNoopPersistent.js
index 32c6dcebcbd3c..8e096f71bfb6e 100644
--- a/packages/react-noop-renderer/src/ReactNoopPersistent.js
+++ b/packages/react-noop-renderer/src/ReactNoopPersistent.js
@@ -14,9 +14,11 @@
* environment.
*/
+import ReactFiberPersistentReconciler from 'react-reconciler/persistent';
import createReactNoop from './createReactNoop';
const ReactNoopPersistent = createReactNoop(
+ ReactFiberPersistentReconciler, // reconciler
false, // useMutation
);
diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js
index 2667122928808..950f0deec29c7 100644
--- a/packages/react-noop-renderer/src/createReactNoop.js
+++ b/packages/react-noop-renderer/src/createReactNoop.js
@@ -18,7 +18,6 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {UpdateQueue} from 'react-reconciler/src/ReactUpdateQueue';
import type {ReactNodeList} from 'shared/ReactTypes';
-import ReactFiberReconciler from 'react-reconciler';
import * as ReactPortal from 'shared/ReactPortal';
import emptyObject from 'fbjs/lib/emptyObject';
import expect from 'expect';
@@ -33,7 +32,7 @@ type Instance = {|
|};
type TextInstance = {|text: string, id: number|};
-function createReactNoop(useMutation: boolean) {
+function createReactNoop(reconciler: Function, useMutation: boolean) {
const UPDATE_SIGNAL = {};
let scheduledCallback = null;
@@ -187,101 +186,105 @@ function createReactNoop(useMutation: boolean) {
},
isPrimaryRenderer: true,
+ supportsHydration: false,
};
const hostConfig = useMutation
? {
...sharedHostConfig,
- mutation: {
- commitMount(instance: Instance, type: string, newProps: Props): void {
- // Noop
- },
-
- commitUpdate(
- instance: Instance,
- updatePayload: Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- ): void {
- if (oldProps === null) {
- throw new Error('Should have old props');
- }
- 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 {},
+
+ supportsMutation: true,
+ supportsPersistence: false,
+
+ commitMount(instance: Instance, type: string, newProps: Props): void {
+ // Noop
+ },
+
+ commitUpdate(
+ instance: Instance,
+ updatePayload: Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ ): void {
+ if (oldProps === null) {
+ throw new Error('Should have old props');
+ }
+ 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 {},
}
: {
...sharedHostConfig,
- persistence: {
- cloneInstance(
- instance: Instance,
- updatePayload: null | Object,
- type: string,
- oldProps: Props,
- newProps: Props,
- internalInstanceHandle: Object,
- keepChildren: boolean,
- recyclableInstance: null | Instance,
- ): Instance {
- const clone = {
- id: instance.id,
- type: type,
- children: keepChildren ? instance.children : [],
- prop: newProps.prop,
- };
- Object.defineProperty(clone, 'id', {
- value: clone.id,
- enumerable: false,
- });
- return clone;
- },
-
- createContainerChildSet(
- container: Container,
- ): Array {
- return [];
- },
-
- appendChildToContainerChildSet(
- childSet: Array,
- child: Instance | TextInstance,
- ): void {
- childSet.push(child);
- },
-
- finalizeContainerChildren(
- container: Container,
- newChildren: Array,
- ): void {},
-
- replaceContainerChildren(
- container: Container,
- newChildren: Array,
- ): void {
- container.children = newChildren;
- },
+ supportsMutation: false,
+ supportsPersistence: true,
+
+ cloneInstance(
+ instance: Instance,
+ updatePayload: null | Object,
+ type: string,
+ oldProps: Props,
+ newProps: Props,
+ internalInstanceHandle: Object,
+ keepChildren: boolean,
+ recyclableInstance: null | Instance,
+ ): Instance {
+ const clone = {
+ id: instance.id,
+ type: type,
+ children: keepChildren ? instance.children : [],
+ prop: newProps.prop,
+ };
+ Object.defineProperty(clone, 'id', {
+ value: clone.id,
+ enumerable: false,
+ });
+ return clone;
+ },
+
+ createContainerChildSet(
+ container: Container,
+ ): Array {
+ return [];
+ },
+
+ appendChildToContainerChildSet(
+ childSet: Array,
+ child: Instance | TextInstance,
+ ): void {
+ childSet.push(child);
+ },
+
+ finalizeContainerChildren(
+ container: Container,
+ newChildren: Array,
+ ): void {},
+
+ replaceContainerChildren(
+ container: Container,
+ newChildren: Array,
+ ): void {
+ container.children = newChildren;
},
};
- const NoopRenderer = ReactFiberReconciler(hostConfig);
+ const NoopRenderer = reconciler(hostConfig);
const rootContainers = new Map();
const roots = new Map();
diff --git a/packages/react-reconciler/index.js b/packages/react-reconciler/index.js
index f513c9c9653c8..5e6b0e53b85fe 100644
--- a/packages/react-reconciler/index.js
+++ b/packages/react-reconciler/index.js
@@ -3,18 +3,21 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- *
- * @flow
*/
-'use strict';
+// This entry point is intentionally not typed. It exists only for third-party
+// renderers. The renderers we ship (such as React DOM) instead import a named
+// "inline" entry point (for example, `react-reconciler/inline.dom`). It uses
+// the same code, but the Flow configuration redirects the host config to its
+// real implementation so we can check it against exact intended host types.
+//
+// Only one renderer (the one you passed to `yarn flow `) is fully
+// type-checked at any given time. The Flow config maps the
+// `react-reconciler/inline.` import (which is *not* Flow typed) to
+// `react-reconciler/inline-typed` (which *is*) for the current renderer.
+// On CI, we run Flow checks for each renderer separately.
-// TODO: bundle Flow types with the package.
-export type {
- HostConfig,
- Deadline,
- Reconciler,
-} from './src/ReactFiberReconciler';
+'use strict';
const ReactFiberReconciler = require('./src/ReactFiberReconciler');
diff --git a/packages/react-reconciler/inline-typed.js b/packages/react-reconciler/inline-typed.js
new file mode 100644
index 0000000000000..958abce94c6cb
--- /dev/null
+++ b/packages/react-reconciler/inline-typed.js
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ *
+ * @flow
+ */
+
+// This file must have the Flow annotation.
+//
+// This is the Flow-typed entry point for the reconciler. It should not be
+// imported directly in code. Instead, our Flow configuration uses this entry
+// point for the currently checked renderer (the one you passed to `yarn flow`).
+//
+// For example, if you run `yarn flow dom`, `react-reconciler/inline.dom` points
+// to this module (and thus will be considered Flow-typed). But other renderers
+// (e.g. `react-test-renderer`) will see reconciler as untyped during the check.
+//
+// We can't make all entry points typed at the same time because different
+// renderers have different host config types. So we check them one by one.
+// We run Flow on all renderers on CI.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/inline.art.js b/packages/react-reconciler/inline.art.js
new file mode 100644
index 0000000000000..0d988dfc1045d
--- /dev/null
+++ b/packages/react-reconciler/inline.art.js
@@ -0,0 +1,11 @@
+/**
+ * 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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/inline.dom.js b/packages/react-reconciler/inline.dom.js
new file mode 100644
index 0000000000000..0d988dfc1045d
--- /dev/null
+++ b/packages/react-reconciler/inline.dom.js
@@ -0,0 +1,11 @@
+/**
+ * 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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/inline.fabric.js b/packages/react-reconciler/inline.fabric.js
new file mode 100644
index 0000000000000..0d988dfc1045d
--- /dev/null
+++ b/packages/react-reconciler/inline.fabric.js
@@ -0,0 +1,11 @@
+/**
+ * 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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/inline.native.js b/packages/react-reconciler/inline.native.js
new file mode 100644
index 0000000000000..0d988dfc1045d
--- /dev/null
+++ b/packages/react-reconciler/inline.native.js
@@ -0,0 +1,11 @@
+/**
+ * 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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/inline.test.js b/packages/react-reconciler/inline.test.js
new file mode 100644
index 0000000000000..0d988dfc1045d
--- /dev/null
+++ b/packages/react-reconciler/inline.test.js
@@ -0,0 +1,11 @@
+/**
+ * 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.
+ */
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+export * from './src/ReactFiberReconciler';
diff --git a/packages/react-reconciler/persistent.js b/packages/react-reconciler/persistent.js
index fb015e101294e..86623952b3f3c 100644
--- a/packages/react-reconciler/persistent.js
+++ b/packages/react-reconciler/persistent.js
@@ -3,8 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- *
- * @flow
*/
'use strict';
diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js
index d344ce0998b42..53ea870e67b61 100644
--- a/packages/react-reconciler/src/ReactFiberBeginWork.js
+++ b/packages/react-reconciler/src/ReactFiberBeginWork.js
@@ -7,16 +7,10 @@
* @flow
*/
-import type {HostConfig} from 'react-reconciler';
import type {ReactProviderType, ReactContext} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
-import type {HostContext} from './ReactFiberHostContext';
-import type {LegacyContext} from './ReactFiberContext';
-import type {NewContext} from './ReactFiberNewContext';
-import type {HydrationContext} from './ReactFiberHydrationContext';
import type {FiberRoot} from './ReactFiberRoot';
import type {ExpirationTime} from './ReactFiberExpirationTime';
-import type {ProfilerTimer} from './ReactProfilerTimer';
import checkPropTypes from 'prop-types/checkPropTypes';
import {
@@ -58,9 +52,7 @@ import warning from 'fbjs/lib/warning';
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
import {cancelWorkTimer} from './ReactDebugFiberPerf';
-import ReactFiberClassComponent, {
- applyDerivedStateFromProps,
-} from './ReactFiberClassComponent';
+import {applyDerivedStateFromProps} from './ReactFiberClassComponent';
import {
mountChildFibers,
reconcileChildFibers,
@@ -69,6 +61,40 @@ import {
import {processUpdateQueue} from './ReactUpdateQueue';
import {NoWork, Never} from './ReactFiberExpirationTime';
import {AsyncMode, StrictMode} from './ReactTypeOfMode';
+import {
+ shouldSetTextContent,
+ shouldDeprioritizeSubtree,
+} from './ReactFiberHostConfig';
+import {pushHostContext, pushHostContainer} from './ReactFiberHostContext';
+import {
+ pushProvider,
+ getContextCurrentValue,
+ getContextChangedBits,
+} from './ReactFiberNewContext';
+import {
+ markActualRenderTimeStarted,
+ stopBaseRenderTimerIfRunning,
+} from './ReactProfilerTimer';
+import {
+ getMaskedContext,
+ getUnmaskedContext,
+ hasContextChanged as hasLegacyContextChanged,
+ pushContextProvider as pushLegacyContextProvider,
+ pushTopLevelContextObject,
+ invalidateContextProvider,
+} from './ReactFiberContext';
+import {
+ enterHydrationState,
+ resetHydrationState,
+ tryToClaimNextHydratableInstance,
+} from './ReactFiberHydrationContext';
+import {
+ adoptClassInstance,
+ constructClassInstance,
+ mountClassInstance,
+ resumeMountClassInstance,
+ updateClassInstance,
+} from './ReactFiberClassComponent';
import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';
const {getCurrentFiberStackAddendum} = ReactDebugCurrentFiber;
@@ -83,869 +109,812 @@ if (__DEV__) {
didWarnAboutStatelessRefs = {};
}
-export default function(
- config: HostConfig,
- hostContext: HostContext,
- legacyContext: LegacyContext,
- newContext: NewContext,
- hydrationContext: HydrationContext,
- scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
- computeExpirationForFiber: (
- startTime: ExpirationTime,
- fiber: Fiber,
- ) => ExpirationTime,
- profilerTimer: ProfilerTimer,
- recalculateCurrentTime: () => ExpirationTime,
-) {
- const {shouldSetTextContent, shouldDeprioritizeSubtree} = config;
-
- const {pushHostContext, pushHostContainer} = hostContext;
-
- const {
- pushProvider,
- getContextCurrentValue,
- getContextChangedBits,
- } = newContext;
-
- const {
- markActualRenderTimeStarted,
- stopBaseRenderTimerIfRunning,
- } = profilerTimer;
-
- const {
- getMaskedContext,
- getUnmaskedContext,
- hasContextChanged: hasLegacyContextChanged,
- pushContextProvider: pushLegacyContextProvider,
- pushTopLevelContextObject,
- invalidateContextProvider,
- } = legacyContext;
-
- const {
- enterHydrationState,
- resetHydrationState,
- tryToClaimNextHydratableInstance,
- } = hydrationContext;
-
- const {
- adoptClassInstance,
- constructClassInstance,
- mountClassInstance,
- resumeMountClassInstance,
- updateClassInstance,
- } = ReactFiberClassComponent(
- legacyContext,
- scheduleWork,
- computeExpirationForFiber,
- memoizeProps,
- memoizeState,
- recalculateCurrentTime,
+// TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
+function reconcileChildren(current, workInProgress, nextChildren) {
+ reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ workInProgress.expirationTime,
);
+}
- // TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
- function reconcileChildren(current, workInProgress, nextChildren) {
- reconcileChildrenAtExpirationTime(
- current,
+function reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ renderExpirationTime,
+) {
+ if (current === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(
workInProgress,
+ null,
nextChildren,
- workInProgress.expirationTime,
+ renderExpirationTime,
+ );
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ current.child,
+ nextChildren,
+ renderExpirationTime,
);
}
+}
- function reconcileChildrenAtExpirationTime(
- current,
- workInProgress,
- nextChildren,
- renderExpirationTime,
- ) {
- if (current === null) {
- // If this is a fresh new component that hasn't been rendered yet, we
- // won't update its child set by applying minimal side-effects. Instead,
- // we will add them all to the child before it gets rendered. That means
- // we can optimize this reconciliation pass by not tracking side-effects.
- workInProgress.child = mountChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderExpirationTime,
- );
- } else {
- // If the current child is the same as the work in progress, it means that
- // we haven't yet started any work on these children. Therefore, we use
- // the clone algorithm to create a copy of all the current children.
-
- // If we had any progressed work already, that is invalid at this point so
- // let's throw it out.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- current.child,
- nextChildren,
- renderExpirationTime,
- );
+function updateForwardRef(current, workInProgress) {
+ const render = workInProgress.type.render;
+ const nextProps = workInProgress.pendingProps;
+ const ref = workInProgress.ref;
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextProps) {
+ const currentRef = current !== null ? current.ref : null;
+ if (ref === currentRef) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
}
- function updateForwardRef(current, workInProgress) {
- const render = workInProgress.type.render;
- const nextProps = workInProgress.pendingProps;
- const ref = workInProgress.ref;
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (workInProgress.memoizedProps === nextProps) {
- const currentRef = current !== null ? current.ref : null;
- if (ref === currentRef) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- }
-
- let nextChildren;
- if (__DEV__) {
- ReactCurrentOwner.current = workInProgress;
- ReactDebugCurrentFiber.setCurrentPhase('render');
- nextChildren = render(nextProps, ref);
- ReactDebugCurrentFiber.setCurrentPhase(null);
- } else {
- nextChildren = render(nextProps, ref);
- }
-
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
+ let nextChildren;
+ if (__DEV__) {
+ ReactCurrentOwner.current = workInProgress;
+ ReactDebugCurrentFiber.setCurrentPhase('render');
+ nextChildren = render(nextProps, ref);
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ } else {
+ nextChildren = render(nextProps, ref);
}
- function updateFragment(current, workInProgress) {
- const nextChildren = workInProgress.pendingProps;
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (workInProgress.memoizedProps === nextChildren) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextChildren);
- return workInProgress.child;
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
+}
+
+function updateFragment(current, workInProgress) {
+ const nextChildren = workInProgress.pendingProps;
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextChildren) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ return workInProgress.child;
+}
- function updateMode(current, workInProgress) {
- const nextChildren = workInProgress.pendingProps.children;
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (
- nextChildren === null ||
- workInProgress.memoizedProps === nextChildren
- ) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextChildren);
- return workInProgress.child;
+function updateMode(current, workInProgress) {
+ const nextChildren = workInProgress.pendingProps.children;
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (
+ nextChildren === null ||
+ workInProgress.memoizedProps === nextChildren
+ ) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ return workInProgress.child;
+}
- function updateProfiler(current, workInProgress) {
- const nextProps = workInProgress.pendingProps;
- if (enableProfilerTimer) {
- // Start render timer here and push start time onto queue
- markActualRenderTimeStarted(workInProgress);
+function updateProfiler(current, workInProgress) {
+ const nextProps = workInProgress.pendingProps;
+ if (enableProfilerTimer) {
+ // Start render timer here and push start time onto queue
+ markActualRenderTimeStarted(workInProgress);
- // Let the "complete" phase know to stop the timer,
- // And the scheduler to record the measured time.
- workInProgress.effectTag |= Update;
- }
- if (workInProgress.memoizedProps === nextProps) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- const nextChildren = nextProps.children;
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
+ // Let the "complete" phase know to stop the timer,
+ // And the scheduler to record the measured time.
+ workInProgress.effectTag |= Update;
+ }
+ if (workInProgress.memoizedProps === nextProps) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
+ const nextChildren = nextProps.children;
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
+}
- function markRef(current: Fiber | null, workInProgress: Fiber) {
- const ref = workInProgress.ref;
- if (
- (current === null && ref !== null) ||
- (current !== null && current.ref !== ref)
- ) {
- // Schedule a Ref effect
- workInProgress.effectTag |= Ref;
- }
+function markRef(current: Fiber | null, workInProgress: Fiber) {
+ const ref = workInProgress.ref;
+ if (
+ (current === null && ref !== null) ||
+ (current !== null && current.ref !== ref)
+ ) {
+ // Schedule a Ref effect
+ workInProgress.effectTag |= Ref;
}
+}
- function updateFunctionalComponent(current, workInProgress) {
- const fn = workInProgress.type;
- const nextProps = workInProgress.pendingProps;
+function updateFunctionalComponent(current, workInProgress) {
+ const fn = workInProgress.type;
+ const nextProps = workInProgress.pendingProps;
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else {
- if (workInProgress.memoizedProps === nextProps) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- // TODO: consider bringing fn.shouldComponentUpdate() back.
- // It used to be here.
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else {
+ if (workInProgress.memoizedProps === nextProps) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
+ // TODO: consider bringing fn.shouldComponentUpdate() back.
+ // It used to be here.
+ }
- const unmaskedContext = getUnmaskedContext(workInProgress);
- const context = getMaskedContext(workInProgress, unmaskedContext);
+ const unmaskedContext = getUnmaskedContext(workInProgress);
+ const context = getMaskedContext(workInProgress, unmaskedContext);
- let nextChildren;
+ let nextChildren;
- if (__DEV__) {
- ReactCurrentOwner.current = workInProgress;
- ReactDebugCurrentFiber.setCurrentPhase('render');
- nextChildren = fn(nextProps, context);
- ReactDebugCurrentFiber.setCurrentPhase(null);
- } else {
- nextChildren = fn(nextProps, context);
- }
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
+ if (__DEV__) {
+ ReactCurrentOwner.current = workInProgress;
+ ReactDebugCurrentFiber.setCurrentPhase('render');
+ nextChildren = fn(nextProps, context);
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ } else {
+ nextChildren = fn(nextProps, context);
}
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
+}
- function updateClassComponent(
- current: Fiber | null,
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ) {
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
- const hasContext = pushLegacyContextProvider(workInProgress);
- let shouldUpdate;
- if (current === null) {
- if (workInProgress.stateNode === null) {
- // In the initial pass we might need to construct the instance.
- constructClassInstance(
- workInProgress,
- workInProgress.pendingProps,
- renderExpirationTime,
- );
- mountClassInstance(workInProgress, renderExpirationTime);
+function updateClassComponent(
+ current: Fiber | null,
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+) {
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ const hasContext = pushLegacyContextProvider(workInProgress);
+ let shouldUpdate;
+ if (current === null) {
+ if (workInProgress.stateNode === null) {
+ // In the initial pass we might need to construct the instance.
+ constructClassInstance(
+ workInProgress,
+ workInProgress.pendingProps,
+ renderExpirationTime,
+ );
+ mountClassInstance(workInProgress, renderExpirationTime);
- shouldUpdate = true;
- } else {
- // In a resume, we'll already have an instance we can reuse.
- shouldUpdate = resumeMountClassInstance(
- workInProgress,
- renderExpirationTime,
- );
- }
+ shouldUpdate = true;
} else {
- shouldUpdate = updateClassInstance(
- current,
+ // In a resume, we'll already have an instance we can reuse.
+ shouldUpdate = resumeMountClassInstance(
workInProgress,
renderExpirationTime,
);
}
- return finishClassComponent(
+ } else {
+ shouldUpdate = updateClassInstance(
current,
workInProgress,
- shouldUpdate,
- hasContext,
renderExpirationTime,
);
}
+ return finishClassComponent(
+ current,
+ workInProgress,
+ shouldUpdate,
+ hasContext,
+ renderExpirationTime,
+ );
+}
- function finishClassComponent(
- current: Fiber | null,
- workInProgress: Fiber,
- shouldUpdate: boolean,
- hasContext: boolean,
- renderExpirationTime: ExpirationTime,
- ) {
- // Refs should update even if shouldComponentUpdate returns false
- markRef(current, workInProgress);
-
- const didCaptureError =
- (workInProgress.effectTag & DidCapture) !== NoEffect;
+function finishClassComponent(
+ current: Fiber | null,
+ workInProgress: Fiber,
+ shouldUpdate: boolean,
+ hasContext: boolean,
+ renderExpirationTime: ExpirationTime,
+) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef(current, workInProgress);
- if (!shouldUpdate && !didCaptureError) {
- // Context providers should defer to sCU for rendering
- if (hasContext) {
- invalidateContextProvider(workInProgress, false);
- }
+ const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ if (!shouldUpdate && !didCaptureError) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, false);
}
- const ctor = workInProgress.type;
- const instance = workInProgress.stateNode;
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
- // Rerender
- ReactCurrentOwner.current = workInProgress;
- let nextChildren;
- if (
- didCaptureError &&
- (!enableGetDerivedStateFromCatch ||
- typeof ctor.getDerivedStateFromCatch !== 'function')
- ) {
- // If we captured an error, but getDerivedStateFrom catch is not defined,
- // unmount all the children. componentDidCatch will schedule an update to
- // re-render a fallback. This is temporary until we migrate everyone to
- // the new API.
- // TODO: Warn in a future release.
- nextChildren = null;
+ const ctor = workInProgress.type;
+ const instance = workInProgress.stateNode;
- if (enableProfilerTimer) {
- stopBaseRenderTimerIfRunning();
+ // Rerender
+ ReactCurrentOwner.current = workInProgress;
+ let nextChildren;
+ if (
+ didCaptureError &&
+ (!enableGetDerivedStateFromCatch ||
+ typeof ctor.getDerivedStateFromCatch !== 'function')
+ ) {
+ // If we captured an error, but getDerivedStateFrom catch is not defined,
+ // unmount all the children. componentDidCatch will schedule an update to
+ // re-render a fallback. This is temporary until we migrate everyone to
+ // the new API.
+ // TODO: Warn in a future release.
+ nextChildren = null;
+
+ if (enableProfilerTimer) {
+ stopBaseRenderTimerIfRunning();
+ }
+ } else {
+ if (__DEV__) {
+ ReactDebugCurrentFiber.setCurrentPhase('render');
+ nextChildren = instance.render();
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.mode & StrictMode)
+ ) {
+ instance.render();
}
+ ReactDebugCurrentFiber.setCurrentPhase(null);
} else {
- if (__DEV__) {
- ReactDebugCurrentFiber.setCurrentPhase('render');
- nextChildren = instance.render();
- if (
- debugRenderPhaseSideEffects ||
- (debugRenderPhaseSideEffectsForStrictMode &&
- workInProgress.mode & StrictMode)
- ) {
- instance.render();
- }
- ReactDebugCurrentFiber.setCurrentPhase(null);
- } else {
- nextChildren = instance.render();
- }
+ nextChildren = instance.render();
}
+ }
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
- if (didCaptureError) {
- // If we're recovering from an error, reconcile twice: first to delete
- // all the existing children.
- reconcileChildrenAtExpirationTime(
- current,
- workInProgress,
- null,
- renderExpirationTime,
- );
- workInProgress.child = null;
- // Now we can continue reconciling like normal. This has the effect of
- // remounting all children regardless of whether their their
- // identity matches.
- }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ if (didCaptureError) {
+ // If we're recovering from an error, reconcile twice: first to delete
+ // all the existing children.
reconcileChildrenAtExpirationTime(
current,
workInProgress,
- nextChildren,
+ null,
renderExpirationTime,
);
- // Memoize props and state using the values we just used to render.
- // TODO: Restructure so we never read values from the instance.
- memoizeState(workInProgress, instance.state);
- memoizeProps(workInProgress, instance.props);
+ workInProgress.child = null;
+ // Now we can continue reconciling like normal. This has the effect of
+ // remounting all children regardless of whether their their
+ // identity matches.
+ }
+ reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ renderExpirationTime,
+ );
+ // Memoize props and state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+ memoizeState(workInProgress, instance.state);
+ memoizeProps(workInProgress, instance.props);
+
+ // The context might have changed so we need to recalculate it.
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, true);
+ }
- // The context might have changed so we need to recalculate it.
- if (hasContext) {
- invalidateContextProvider(workInProgress, true);
- }
+ return workInProgress.child;
+}
- return workInProgress.child;
+function pushHostRootContext(workInProgress) {
+ const root = (workInProgress.stateNode: FiberRoot);
+ if (root.pendingContext) {
+ pushTopLevelContextObject(
+ workInProgress,
+ root.pendingContext,
+ root.pendingContext !== root.context,
+ );
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
}
+ pushHostContainer(workInProgress, root.containerInfo);
+}
- function pushHostRootContext(workInProgress) {
- const root = (workInProgress.stateNode: FiberRoot);
- if (root.pendingContext) {
- pushTopLevelContextObject(
- workInProgress,
- root.pendingContext,
- root.pendingContext !== root.context,
- );
- } else if (root.context) {
- // Should always be set
- pushTopLevelContextObject(workInProgress, root.context, false);
+function updateHostRoot(current, workInProgress, renderExpirationTime) {
+ pushHostRootContext(workInProgress);
+ let updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ const nextProps = workInProgress.pendingProps;
+ const prevState = workInProgress.memoizedState;
+ const prevChildren = prevState !== null ? prevState.element : null;
+ processUpdateQueue(
+ workInProgress,
+ updateQueue,
+ nextProps,
+ null,
+ renderExpirationTime,
+ );
+ const nextState = workInProgress.memoizedState;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ const nextChildren = nextState.element;
+
+ if (nextChildren === prevChildren) {
+ // If the state is the same as before, that's a bailout because we had
+ // no work that expires at this time.
+ resetHydrationState();
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
- pushHostContainer(workInProgress, root.containerInfo);
- }
-
- function updateHostRoot(current, workInProgress, renderExpirationTime) {
- pushHostRootContext(workInProgress);
- let updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- const nextProps = workInProgress.pendingProps;
- const prevState = workInProgress.memoizedState;
- const prevChildren = prevState !== null ? prevState.element : null;
- processUpdateQueue(
+ const root: FiberRoot = workInProgress.stateNode;
+ if (
+ (current === null || current.child === null) &&
+ root.hydrate &&
+ enterHydrationState(workInProgress)
+ ) {
+ // If we don't have any current children this might be the first pass.
+ // We always try to hydrate. If this isn't a hydration pass there won't
+ // be any children to hydrate which is effectively the same thing as
+ // not hydrating.
+
+ // This is a bit of a hack. We track the host root as a placement to
+ // know that we're currently in a mounting state. That way isMounted
+ // works as expected. We must reset this before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag |= Placement;
+
+ // Ensure that children mount into this root without tracking
+ // side-effects. This ensures that we don't store Placement effects on
+ // nodes that will be hydrated.
+ workInProgress.child = mountChildFibers(
workInProgress,
- updateQueue,
- nextProps,
null,
+ nextChildren,
renderExpirationTime,
);
- const nextState = workInProgress.memoizedState;
- // Caution: React DevTools currently depends on this property
- // being called "element".
- const nextChildren = nextState.element;
-
- if (nextChildren === prevChildren) {
- // If the state is the same as before, that's a bailout because we had
- // no work that expires at this time.
- resetHydrationState();
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- const root: FiberRoot = workInProgress.stateNode;
- if (
- (current === null || current.child === null) &&
- root.hydrate &&
- enterHydrationState(workInProgress)
- ) {
- // If we don't have any current children this might be the first pass.
- // We always try to hydrate. If this isn't a hydration pass there won't
- // be any children to hydrate which is effectively the same thing as
- // not hydrating.
-
- // This is a bit of a hack. We track the host root as a placement to
- // know that we're currently in a mounting state. That way isMounted
- // works as expected. We must reset this before committing.
- // TODO: Delete this when we delete isMounted and findDOMNode.
- workInProgress.effectTag |= Placement;
-
- // Ensure that children mount into this root without tracking
- // side-effects. This ensures that we don't store Placement effects on
- // nodes that will be hydrated.
- workInProgress.child = mountChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderExpirationTime,
- );
- } else {
- // Otherwise reset hydration state in case we aborted and resumed another
- // root.
- resetHydrationState();
- reconcileChildren(current, workInProgress, nextChildren);
- }
- return workInProgress.child;
+ } else {
+ // Otherwise reset hydration state in case we aborted and resumed another
+ // root.
+ resetHydrationState();
+ reconcileChildren(current, workInProgress, nextChildren);
}
- resetHydrationState();
- // If there is no update queue, that's a bailout because the root has no props.
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ return workInProgress.child;
}
+ resetHydrationState();
+ // If there is no update queue, that's a bailout because the root has no props.
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+}
- function updateHostComponent(current, workInProgress, renderExpirationTime) {
- pushHostContext(workInProgress);
-
- if (current === null) {
- tryToClaimNextHydratableInstance(workInProgress);
- }
-
- const type = workInProgress.type;
- const memoizedProps = workInProgress.memoizedProps;
- const nextProps = workInProgress.pendingProps;
- const prevProps = current !== null ? current.memoizedProps : null;
-
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (memoizedProps === nextProps) {
- const isHidden =
- workInProgress.mode & AsyncMode &&
- shouldDeprioritizeSubtree(type, nextProps);
- if (isHidden) {
- // Before bailing out, make sure we've deprioritized a hidden component.
- workInProgress.expirationTime = Never;
- }
- if (!isHidden || renderExpirationTime !== Never) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- // If we're rendering a hidden node at hidden priority, don't bailout. The
- // parent is complete, but the children may not be.
- }
+function updateHostComponent(current, workInProgress, renderExpirationTime) {
+ pushHostContext(workInProgress);
- let nextChildren = nextProps.children;
- const isDirectTextChild = shouldSetTextContent(type, nextProps);
-
- if (isDirectTextChild) {
- // We special case a direct text child of a host node. This is a common
- // case. We won't handle it as a reified child. We will instead handle
- // this in the host environment that also have access to this prop. That
- // avoids allocating another HostText fiber and traversing it.
- nextChildren = null;
- } else if (prevProps && shouldSetTextContent(type, prevProps)) {
- // If we're switching from a direct text child to a normal child, or to
- // empty, we need to schedule the text content to be reset.
- workInProgress.effectTag |= ContentReset;
- }
+ if (current === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
- markRef(current, workInProgress);
+ const type = workInProgress.type;
+ const memoizedProps = workInProgress.memoizedProps;
+ const nextProps = workInProgress.pendingProps;
+ const prevProps = current !== null ? current.memoizedProps : null;
- // Check the host config to see if the children are offscreen/hidden.
- if (
- renderExpirationTime !== Never &&
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (memoizedProps === nextProps) {
+ const isHidden =
workInProgress.mode & AsyncMode &&
- shouldDeprioritizeSubtree(type, nextProps)
- ) {
- // Down-prioritize the children.
+ shouldDeprioritizeSubtree(type, nextProps);
+ if (isHidden) {
+ // Before bailing out, make sure we've deprioritized a hidden component.
workInProgress.expirationTime = Never;
- // Bailout and come back to this fiber later.
- workInProgress.memoizedProps = nextProps;
- return null;
}
+ if (!isHidden || renderExpirationTime !== Never) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ // If we're rendering a hidden node at hidden priority, don't bailout. The
+ // parent is complete, but the children may not be.
+ }
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
+ let nextChildren = nextProps.children;
+ const isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also have access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.effectTag |= ContentReset;
}
- function updateHostText(current, workInProgress) {
- if (current === null) {
- tryToClaimNextHydratableInstance(workInProgress);
- }
- const nextProps = workInProgress.pendingProps;
- memoizeProps(workInProgress, nextProps);
- // Nothing to do here. This is terminal. We'll do the completion step
- // immediately after.
+ markRef(current, workInProgress);
+
+ // Check the host config to see if the children are offscreen/hidden.
+ if (
+ renderExpirationTime !== Never &&
+ workInProgress.mode & AsyncMode &&
+ shouldDeprioritizeSubtree(type, nextProps)
+ ) {
+ // Down-prioritize the children.
+ workInProgress.expirationTime = Never;
+ // Bailout and come back to this fiber later.
+ workInProgress.memoizedProps = nextProps;
return null;
}
- function mountIndeterminateComponent(
- current,
- workInProgress,
- renderExpirationTime,
- ) {
- invariant(
- current === null,
- 'An indeterminate component should never have mounted. This error is ' +
- 'likely caused by a bug in React. Please file an issue.',
- );
- const fn = workInProgress.type;
- const props = workInProgress.pendingProps;
- const unmaskedContext = getUnmaskedContext(workInProgress);
- const context = getMaskedContext(workInProgress, unmaskedContext);
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
+}
- let value;
+function updateHostText(current, workInProgress) {
+ if (current === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+ const nextProps = workInProgress.pendingProps;
+ memoizeProps(workInProgress, nextProps);
+ // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+ return null;
+}
- if (__DEV__) {
- if (fn.prototype && typeof fn.prototype.render === 'function') {
- const componentName = getComponentName(workInProgress) || 'Unknown';
+function mountIndeterminateComponent(
+ current,
+ workInProgress,
+ renderExpirationTime,
+) {
+ invariant(
+ current === null,
+ 'An indeterminate component should never have mounted. This error is ' +
+ 'likely caused by a bug in React. Please file an issue.',
+ );
+ const fn = workInProgress.type;
+ const props = workInProgress.pendingProps;
+ const unmaskedContext = getUnmaskedContext(workInProgress);
+ const context = getMaskedContext(workInProgress, unmaskedContext);
- if (!didWarnAboutBadClass[componentName]) {
- warning(
- false,
- "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
- 'This is likely to cause errors. Change %s to extend React.Component instead.',
- componentName,
- componentName,
- );
- didWarnAboutBadClass[componentName] = true;
- }
+ let value;
+
+ if (__DEV__) {
+ if (fn.prototype && typeof fn.prototype.render === 'function') {
+ const componentName = getComponentName(workInProgress) || 'Unknown';
+
+ if (!didWarnAboutBadClass[componentName]) {
+ warning(
+ false,
+ "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
+ 'This is likely to cause errors. Change %s to extend React.Component instead.',
+ componentName,
+ componentName,
+ );
+ didWarnAboutBadClass[componentName] = true;
}
- ReactCurrentOwner.current = workInProgress;
- value = fn(props, context);
- } else {
- value = fn(props, context);
}
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
+ ReactCurrentOwner.current = workInProgress;
+ value = fn(props, context);
+ } else {
+ value = fn(props, context);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+
+ if (
+ typeof value === 'object' &&
+ value !== null &&
+ typeof value.render === 'function' &&
+ value.$$typeof === undefined
+ ) {
+ const Component = workInProgress.type;
- if (
- typeof value === 'object' &&
- value !== null &&
- typeof value.render === 'function' &&
- value.$$typeof === undefined
- ) {
- const Component = workInProgress.type;
+ // Proceed under the assumption that this is a class instance
+ workInProgress.tag = ClassComponent;
- // Proceed under the assumption that this is a class instance
- workInProgress.tag = ClassComponent;
+ workInProgress.memoizedState =
+ value.state !== null && value.state !== undefined ? value.state : null;
+
+ const getDerivedStateFromProps = Component.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(
+ workInProgress,
+ getDerivedStateFromProps,
+ props,
+ );
+ }
- workInProgress.memoizedState =
- value.state !== null && value.state !== undefined ? value.state : null;
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ const hasContext = pushLegacyContextProvider(workInProgress);
+ adoptClassInstance(workInProgress, value);
+ mountClassInstance(workInProgress, renderExpirationTime);
+ return finishClassComponent(
+ current,
+ workInProgress,
+ true,
+ hasContext,
+ renderExpirationTime,
+ );
+ } else {
+ // Proceed under the assumption that this is a functional component
+ workInProgress.tag = FunctionalComponent;
+ if (__DEV__) {
+ const Component = workInProgress.type;
- const getDerivedStateFromProps = Component.getDerivedStateFromProps;
- if (typeof getDerivedStateFromProps === 'function') {
- applyDerivedStateFromProps(
- workInProgress,
- getDerivedStateFromProps,
- props,
+ if (Component) {
+ warning(
+ !Component.childContextTypes,
+ '%s(...): childContextTypes cannot be defined on a functional component.',
+ Component.displayName || Component.name || 'Component',
);
}
+ if (workInProgress.ref !== null) {
+ let info = '';
+ const ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName();
+ if (ownerName) {
+ info += '\n\nCheck the render method of `' + ownerName + '`.';
+ }
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
- const hasContext = pushLegacyContextProvider(workInProgress);
- adoptClassInstance(workInProgress, value);
- mountClassInstance(workInProgress, renderExpirationTime);
- return finishClassComponent(
- current,
- workInProgress,
- true,
- hasContext,
- renderExpirationTime,
- );
- } else {
- // Proceed under the assumption that this is a functional component
- workInProgress.tag = FunctionalComponent;
- if (__DEV__) {
- const Component = workInProgress.type;
-
- if (Component) {
+ let warningKey = ownerName || workInProgress._debugID || '';
+ const debugSource = workInProgress._debugSource;
+ if (debugSource) {
+ warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
+ }
+ if (!didWarnAboutStatelessRefs[warningKey]) {
+ didWarnAboutStatelessRefs[warningKey] = true;
warning(
- !Component.childContextTypes,
- '%s(...): childContextTypes cannot be defined on a functional component.',
- Component.displayName || Component.name || 'Component',
+ false,
+ 'Stateless function components cannot be given refs. ' +
+ 'Attempts to access this ref will fail.%s%s',
+ info,
+ ReactDebugCurrentFiber.getCurrentFiberStackAddendum(),
);
}
- if (workInProgress.ref !== null) {
- let info = '';
- const ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName();
- if (ownerName) {
- info += '\n\nCheck the render method of `' + ownerName + '`.';
- }
+ }
- let warningKey = ownerName || workInProgress._debugID || '';
- const debugSource = workInProgress._debugSource;
- if (debugSource) {
- warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
- }
- if (!didWarnAboutStatelessRefs[warningKey]) {
- didWarnAboutStatelessRefs[warningKey] = true;
- warning(
- false,
- 'Stateless function components cannot be given refs. ' +
- 'Attempts to access this ref will fail.%s%s',
- info,
- ReactDebugCurrentFiber.getCurrentFiberStackAddendum(),
- );
- }
- }
+ if (typeof fn.getDerivedStateFromProps === 'function') {
+ const componentName = getComponentName(workInProgress) || 'Unknown';
- if (typeof fn.getDerivedStateFromProps === 'function') {
- const componentName = getComponentName(workInProgress) || 'Unknown';
-
- if (
- !didWarnAboutGetDerivedStateOnFunctionalComponent[componentName]
- ) {
- warning(
- false,
- '%s: Stateless functional components do not support getDerivedStateFromProps.',
- componentName,
- );
- didWarnAboutGetDerivedStateOnFunctionalComponent[
- componentName
- ] = true;
- }
+ if (!didWarnAboutGetDerivedStateOnFunctionalComponent[componentName]) {
+ warning(
+ false,
+ '%s: Stateless functional components do not support getDerivedStateFromProps.',
+ componentName,
+ );
+ didWarnAboutGetDerivedStateOnFunctionalComponent[
+ componentName
+ ] = true;
}
}
- reconcileChildren(current, workInProgress, value);
- memoizeProps(workInProgress, props);
- return workInProgress.child;
}
+ reconcileChildren(current, workInProgress, value);
+ memoizeProps(workInProgress, props);
+ return workInProgress.child;
}
+}
- function updateTimeoutComponent(
- current,
- workInProgress,
- renderExpirationTime,
- ) {
- if (enableSuspense) {
- const nextProps = workInProgress.pendingProps;
- const prevProps = workInProgress.memoizedProps;
-
- const prevDidTimeout = workInProgress.memoizedState;
-
- // Check if we already attempted to render the normal state. If we did,
- // and we timed out, render the placeholder state.
- const alreadyCaptured =
- (workInProgress.effectTag & DidCapture) === NoEffect;
- const nextDidTimeout = !alreadyCaptured;
-
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (nextProps === prevProps && nextDidTimeout === prevDidTimeout) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
+function updateTimeoutComponent(current, workInProgress, renderExpirationTime) {
+ if (enableSuspense) {
+ const nextProps = workInProgress.pendingProps;
+ const prevProps = workInProgress.memoizedProps;
- const render = nextProps.children;
- const nextChildren = render(nextDidTimeout);
- workInProgress.memoizedProps = nextProps;
- workInProgress.memoizedState = nextDidTimeout;
- reconcileChildren(current, workInProgress, nextChildren);
- return workInProgress.child;
- } else {
- return null;
- }
- }
+ const prevDidTimeout = workInProgress.memoizedState;
+
+ // Check if we already attempted to render the normal state. If we did,
+ // and we timed out, render the placeholder state.
+ const alreadyCaptured =
+ (workInProgress.effectTag & DidCapture) === NoEffect;
+ const nextDidTimeout = !alreadyCaptured;
- function updatePortalComponent(
- current,
- workInProgress,
- renderExpirationTime,
- ) {
- pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
- const nextChildren = workInProgress.pendingProps;
if (hasLegacyContextChanged()) {
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
- } else if (workInProgress.memoizedProps === nextChildren) {
+ } else if (nextProps === prevProps && nextDidTimeout === prevDidTimeout) {
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
- if (current === null) {
- // Portals are special because we don't append the children during mount
- // but at commit. Therefore we need to track insertions which the normal
- // flow doesn't do during mount. This doesn't happen at the root because
- // the root always starts with a "current" with a null child.
- // TODO: Consider unifying this with how the root works.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderExpirationTime,
- );
- memoizeProps(workInProgress, nextChildren);
- } else {
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextChildren);
- }
+ const render = nextProps.children;
+ const nextChildren = render(nextDidTimeout);
+ workInProgress.memoizedProps = nextProps;
+ workInProgress.memoizedState = nextDidTimeout;
+ reconcileChildren(current, workInProgress, nextChildren);
return workInProgress.child;
+ } else {
+ return null;
}
+}
- function propagateContextChange(
- workInProgress: Fiber,
- context: ReactContext,
- changedBits: number,
- renderExpirationTime: ExpirationTime,
- ): void {
- let fiber = workInProgress.child;
- if (fiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- fiber.return = workInProgress;
- }
- while (fiber !== null) {
- let nextFiber;
- // Visit this fiber.
- switch (fiber.tag) {
- case ContextConsumer:
- // Check if the context matches.
- const observedBits: number = fiber.stateNode | 0;
- if (fiber.type === context && (observedBits & changedBits) !== 0) {
- // Update the expiration time of all the ancestors, including
- // the alternates.
- let node = fiber;
- while (node !== null) {
- const alternate = node.alternate;
+function updatePortalComponent(current, workInProgress, renderExpirationTime) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ const nextChildren = workInProgress.pendingProps;
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextChildren) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+
+ if (current === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderExpirationTime,
+ );
+ memoizeProps(workInProgress, nextChildren);
+ } else {
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ }
+ return workInProgress.child;
+}
+
+function propagateContextChange(
+ workInProgress: Fiber,
+ context: ReactContext,
+ changedBits: number,
+ renderExpirationTime: ExpirationTime,
+): void {
+ let fiber = workInProgress.child;
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
+ while (fiber !== null) {
+ let nextFiber;
+ // Visit this fiber.
+ switch (fiber.tag) {
+ case ContextConsumer:
+ // Check if the context matches.
+ const observedBits: number = fiber.stateNode | 0;
+ if (fiber.type === context && (observedBits & changedBits) !== 0) {
+ // Update the expiration time of all the ancestors, including
+ // the alternates.
+ let node = fiber;
+ while (node !== null) {
+ const alternate = node.alternate;
+ if (
+ node.expirationTime === NoWork ||
+ node.expirationTime > renderExpirationTime
+ ) {
+ node.expirationTime = renderExpirationTime;
if (
- node.expirationTime === NoWork ||
- node.expirationTime > renderExpirationTime
- ) {
- node.expirationTime = renderExpirationTime;
- if (
- alternate !== null &&
- (alternate.expirationTime === NoWork ||
- alternate.expirationTime > renderExpirationTime)
- ) {
- alternate.expirationTime = renderExpirationTime;
- }
- } else if (
alternate !== null &&
(alternate.expirationTime === NoWork ||
alternate.expirationTime > renderExpirationTime)
) {
alternate.expirationTime = renderExpirationTime;
- } else {
- // Neither alternate was updated, which means the rest of the
- // ancestor path already has sufficient priority.
- break;
}
- node = node.return;
+ } else if (
+ alternate !== null &&
+ (alternate.expirationTime === NoWork ||
+ alternate.expirationTime > renderExpirationTime)
+ ) {
+ alternate.expirationTime = renderExpirationTime;
+ } else {
+ // Neither alternate was updated, which means the rest of the
+ // ancestor path already has sufficient priority.
+ break;
}
- // Don't scan deeper than a matching consumer. When we render the
- // consumer, we'll continue scanning from that point. This way the
- // scanning work is time-sliced.
- nextFiber = null;
- } else {
- // Traverse down.
- nextFiber = fiber.child;
+ node = node.return;
}
- break;
- case ContextProvider:
- // Don't scan deeper if this is a matching provider
- nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
- break;
- default:
+ // Don't scan deeper than a matching consumer. When we render the
+ // consumer, we'll continue scanning from that point. This way the
+ // scanning work is time-sliced.
+ nextFiber = null;
+ } else {
// Traverse down.
nextFiber = fiber.child;
+ }
+ break;
+ case ContextProvider:
+ // Don't scan deeper if this is a matching provider
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
+ break;
+ default:
+ // Traverse down.
+ nextFiber = fiber.child;
+ break;
+ }
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
+ let sibling = nextFiber.sibling;
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
break;
- }
- if (nextFiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- nextFiber.return = fiber;
- } else {
- // No child. Traverse to next sibling.
- nextFiber = fiber;
- while (nextFiber !== null) {
- if (nextFiber === workInProgress) {
- // We're back to the root of this subtree. Exit.
- nextFiber = null;
- break;
- }
- let sibling = nextFiber.sibling;
- if (sibling !== null) {
- // Set the return pointer of the sibling to the work-in-progress fiber.
- sibling.return = nextFiber.return;
- nextFiber = sibling;
- break;
- }
- // No more siblings. Traverse up.
- nextFiber = nextFiber.return;
}
+ // No more siblings. Traverse up.
+ nextFiber = nextFiber.return;
}
- fiber = nextFiber;
}
+ fiber = nextFiber;
}
+}
- function updateContextProvider(
- current,
- workInProgress,
- renderExpirationTime,
- ) {
- const providerType: ReactProviderType = workInProgress.type;
- const context: ReactContext = providerType._context;
+function updateContextProvider(current, workInProgress, renderExpirationTime) {
+ const providerType: ReactProviderType = workInProgress.type;
+ const context: ReactContext = providerType._context;
- const newProps = workInProgress.pendingProps;
- const oldProps = workInProgress.memoizedProps;
- let canBailOnProps = true;
+ const newProps = workInProgress.pendingProps;
+ const oldProps = workInProgress.memoizedProps;
+ let canBailOnProps = true;
- if (hasLegacyContextChanged()) {
- canBailOnProps = false;
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (oldProps === newProps) {
- workInProgress.stateNode = 0;
- pushProvider(workInProgress);
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
+ if (hasLegacyContextChanged()) {
+ canBailOnProps = false;
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (oldProps === newProps) {
+ workInProgress.stateNode = 0;
+ pushProvider(workInProgress);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
- const newValue = newProps.value;
- workInProgress.memoizedProps = newProps;
+ const newValue = newProps.value;
+ workInProgress.memoizedProps = newProps;
- if (__DEV__) {
- const providerPropTypes = workInProgress.type.propTypes;
-
- if (providerPropTypes) {
- checkPropTypes(
- providerPropTypes,
- newProps,
- 'prop',
- 'Context.Provider',
- getCurrentFiberStackAddendum,
- );
- }
+ if (__DEV__) {
+ const providerPropTypes = workInProgress.type.propTypes;
+
+ if (providerPropTypes) {
+ checkPropTypes(
+ providerPropTypes,
+ newProps,
+ 'prop',
+ 'Context.Provider',
+ getCurrentFiberStackAddendum,
+ );
}
+ }
- let changedBits: number;
- if (oldProps === null) {
- // Initial render
- changedBits = MAX_SIGNED_31_BIT_INT;
+ let changedBits: number;
+ if (oldProps === null) {
+ // Initial render
+ changedBits = MAX_SIGNED_31_BIT_INT;
+ } else {
+ if (oldProps.value === newProps.value) {
+ // No change. Bailout early if children are the same.
+ if (oldProps.children === newProps.children && canBailOnProps) {
+ workInProgress.stateNode = 0;
+ pushProvider(workInProgress);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ changedBits = 0;
} else {
- if (oldProps.value === newProps.value) {
+ const oldValue = oldProps.value;
+ // Use Object.is to compare the new context value to the old value.
+ // Inlined Object.is polyfill.
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ if (
+ (oldValue === newValue &&
+ (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
+ (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
+ ) {
// No change. Bailout early if children are the same.
if (oldProps.children === newProps.children && canBailOnProps) {
workInProgress.stateNode = 0;
@@ -954,139 +923,118 @@ export default function(
}
changedBits = 0;
} else {
- const oldValue = oldProps.value;
- // Use Object.is to compare the new context value to the old value.
- // Inlined Object.is polyfill.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- if (
- (oldValue === newValue &&
- (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
- (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
- ) {
+ changedBits =
+ typeof context._calculateChangedBits === 'function'
+ ? context._calculateChangedBits(oldValue, newValue)
+ : MAX_SIGNED_31_BIT_INT;
+ if (__DEV__) {
+ warning(
+ (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
+ 'calculateChangedBits: Expected the return value to be a ' +
+ '31-bit integer. Instead received: %s',
+ changedBits,
+ );
+ }
+ changedBits |= 0;
+
+ if (changedBits === 0) {
// No change. Bailout early if children are the same.
if (oldProps.children === newProps.children && canBailOnProps) {
workInProgress.stateNode = 0;
pushProvider(workInProgress);
return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
- changedBits = 0;
} else {
- changedBits =
- typeof context._calculateChangedBits === 'function'
- ? context._calculateChangedBits(oldValue, newValue)
- : MAX_SIGNED_31_BIT_INT;
- if (__DEV__) {
- warning(
- (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
- 'calculateChangedBits: Expected the return value to be a ' +
- '31-bit integer. Instead received: %s',
- changedBits,
- );
- }
- changedBits |= 0;
-
- if (changedBits === 0) {
- // No change. Bailout early if children are the same.
- if (oldProps.children === newProps.children && canBailOnProps) {
- workInProgress.stateNode = 0;
- pushProvider(workInProgress);
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- } else {
- propagateContextChange(
- workInProgress,
- context,
- changedBits,
- renderExpirationTime,
- );
- }
+ propagateContextChange(
+ workInProgress,
+ context,
+ changedBits,
+ renderExpirationTime,
+ );
}
}
}
-
- workInProgress.stateNode = changedBits;
- pushProvider(workInProgress);
-
- const newChildren = newProps.children;
- reconcileChildren(current, workInProgress, newChildren);
- return workInProgress.child;
}
- function updateContextConsumer(
- current,
- workInProgress,
- renderExpirationTime,
- ) {
- const context: ReactContext = workInProgress.type;
- const newProps = workInProgress.pendingProps;
- const oldProps = workInProgress.memoizedProps;
+ workInProgress.stateNode = changedBits;
+ pushProvider(workInProgress);
- const newValue = getContextCurrentValue(context);
- const changedBits = getContextChangedBits(context);
-
- if (hasLegacyContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (changedBits === 0 && oldProps === newProps) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- workInProgress.memoizedProps = newProps;
+ const newChildren = newProps.children;
+ reconcileChildren(current, workInProgress, newChildren);
+ return workInProgress.child;
+}
- let observedBits = newProps.unstable_observedBits;
- if (observedBits === undefined || observedBits === null) {
- // Subscribe to all changes by default
- observedBits = MAX_SIGNED_31_BIT_INT;
- }
- // Store the observedBits on the fiber's stateNode for quick access.
- workInProgress.stateNode = observedBits;
+function updateContextConsumer(current, workInProgress, renderExpirationTime) {
+ const context: ReactContext = workInProgress.type;
+ const newProps = workInProgress.pendingProps;
+ const oldProps = workInProgress.memoizedProps;
- if ((changedBits & observedBits) !== 0) {
- // Context change propagation stops at matching consumers, for time-
- // slicing. Continue the propagation here.
- propagateContextChange(
- workInProgress,
- context,
- changedBits,
- renderExpirationTime,
- );
- } else if (oldProps === newProps) {
- // Skip over a memoized parent with a bitmask bailout even
- // if we began working on it because of a deeper matching child.
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- // There is no bailout on `children` equality because we expect people
- // to often pass a bound method as a child, but it may reference
- // `this.state` or `this.props` (and thus needs to re-render on `setState`).
+ const newValue = getContextCurrentValue(context);
+ const changedBits = getContextChangedBits(context);
- const render = newProps.children;
+ if (hasLegacyContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (changedBits === 0 && oldProps === newProps) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ workInProgress.memoizedProps = newProps;
- if (__DEV__) {
- warning(
- typeof render === 'function',
- 'A context consumer was rendered with multiple children, or a child ' +
- "that isn't a function. A context consumer expects a single child " +
- 'that is a function. If you did pass a function, make sure there ' +
- 'is no trailing or leading whitespace around it.',
- );
- }
+ let observedBits = newProps.unstable_observedBits;
+ if (observedBits === undefined || observedBits === null) {
+ // Subscribe to all changes by default
+ observedBits = MAX_SIGNED_31_BIT_INT;
+ }
+ // Store the observedBits on the fiber's stateNode for quick access.
+ workInProgress.stateNode = observedBits;
- let newChildren;
- if (__DEV__) {
- ReactCurrentOwner.current = workInProgress;
- ReactDebugCurrentFiber.setCurrentPhase('render');
- newChildren = render(newValue);
- ReactDebugCurrentFiber.setCurrentPhase(null);
- } else {
- newChildren = render(newValue);
- }
+ if ((changedBits & observedBits) !== 0) {
+ // Context change propagation stops at matching consumers, for time-
+ // slicing. Continue the propagation here.
+ propagateContextChange(
+ workInProgress,
+ context,
+ changedBits,
+ renderExpirationTime,
+ );
+ } else if (oldProps === newProps) {
+ // Skip over a memoized parent with a bitmask bailout even
+ // if we began working on it because of a deeper matching child.
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ // There is no bailout on `children` equality because we expect people
+ // to often pass a bound method as a child, but it may reference
+ // `this.state` or `this.props` (and thus needs to re-render on `setState`).
+
+ const render = newProps.children;
+
+ if (__DEV__) {
+ warning(
+ typeof render === 'function',
+ 'A context consumer was rendered with multiple children, or a child ' +
+ "that isn't a function. A context consumer expects a single child " +
+ 'that is a function. If you did pass a function, make sure there ' +
+ 'is no trailing or leading whitespace around it.',
+ );
+ }
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
- reconcileChildren(current, workInProgress, newChildren);
- return workInProgress.child;
+ let newChildren;
+ if (__DEV__) {
+ ReactCurrentOwner.current = workInProgress;
+ ReactDebugCurrentFiber.setCurrentPhase('render');
+ newChildren = render(newValue);
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ } else {
+ newChildren = render(newValue);
}
- /*
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current, workInProgress, newChildren);
+ return workInProgress.child;
+}
+
+/*
function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
let child = firstChild;
do {
@@ -1105,162 +1053,152 @@ export default function(
}
*/
- function bailoutOnAlreadyFinishedWork(
- current,
- workInProgress: Fiber,
- ): Fiber | null {
- cancelWorkTimer(workInProgress);
-
- if (enableProfilerTimer) {
- // Don't update "base" render times for bailouts.
- stopBaseRenderTimerIfRunning();
- }
+function bailoutOnAlreadyFinishedWork(
+ current,
+ workInProgress: Fiber,
+): Fiber | null {
+ cancelWorkTimer(workInProgress);
- // TODO: We should ideally be able to bail out early if the children have no
- // more work to do. However, since we don't have a separation of this
- // Fiber's priority and its children yet - we don't know without doing lots
- // of the same work we do anyway. Once we have that separation we can just
- // bail out here if the children has no more work at this priority level.
- // if (workInProgress.priorityOfChildren <= priorityLevel) {
- // // If there are side-effects in these children that have not yet been
- // // committed we need to ensure that they get properly transferred up.
- // if (current && current.child !== workInProgress.child) {
- // reuseChildrenEffects(workInProgress, child);
- // }
- // return null;
- // }
-
- cloneChildFibers(current, workInProgress);
- return workInProgress.child;
+ if (enableProfilerTimer) {
+ // Don't update "base" render times for bailouts.
+ stopBaseRenderTimerIfRunning();
}
- function bailoutOnLowPriority(current, workInProgress) {
- cancelWorkTimer(workInProgress);
+ // TODO: We should ideally be able to bail out early if the children have no
+ // more work to do. However, since we don't have a separation of this
+ // Fiber's priority and its children yet - we don't know without doing lots
+ // of the same work we do anyway. Once we have that separation we can just
+ // bail out here if the children has no more work at this priority level.
+ // if (workInProgress.priorityOfChildren <= priorityLevel) {
+ // // If there are side-effects in these children that have not yet been
+ // // committed we need to ensure that they get properly transferred up.
+ // if (current && current.child !== workInProgress.child) {
+ // reuseChildrenEffects(workInProgress, child);
+ // }
+ // return null;
+ // }
+
+ cloneChildFibers(current, workInProgress);
+ return workInProgress.child;
+}
- if (enableProfilerTimer) {
- // Don't update "base" render times for bailouts.
- stopBaseRenderTimerIfRunning();
- }
+function bailoutOnLowPriority(current, workInProgress) {
+ cancelWorkTimer(workInProgress);
- // TODO: Handle HostComponent tags here as well and call pushHostContext()?
- // See PR 8590 discussion for context
- switch (workInProgress.tag) {
- case HostRoot:
- pushHostRootContext(workInProgress);
- break;
- case ClassComponent:
- pushLegacyContextProvider(workInProgress);
- break;
- case HostPortal:
- pushHostContainer(
- workInProgress,
- workInProgress.stateNode.containerInfo,
- );
- break;
- case ContextProvider:
- pushProvider(workInProgress);
- break;
- case Profiler:
- if (enableProfilerTimer) {
- markActualRenderTimeStarted(workInProgress);
- }
- break;
- }
- // TODO: What if this is currently in progress?
- // How can that happen? How is this not being cloned?
- return null;
+ if (enableProfilerTimer) {
+ // Don't update "base" render times for bailouts.
+ stopBaseRenderTimerIfRunning();
}
- // TODO: Delete memoizeProps/State and move to reconcile/bailout instead
- function memoizeProps(workInProgress: Fiber, nextProps: any) {
- workInProgress.memoizedProps = nextProps;
+ // TODO: Handle HostComponent tags here as well and call pushHostContext()?
+ // See PR 8590 discussion for context
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ break;
+ case ClassComponent:
+ pushLegacyContextProvider(workInProgress);
+ break;
+ case HostPortal:
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ break;
+ case ContextProvider:
+ pushProvider(workInProgress);
+ break;
+ case Profiler:
+ if (enableProfilerTimer) {
+ markActualRenderTimeStarted(workInProgress);
+ }
+ break;
}
+ // TODO: What if this is currently in progress?
+ // How can that happen? How is this not being cloned?
+ return null;
+}
- function memoizeState(workInProgress: Fiber, nextState: any) {
- workInProgress.memoizedState = nextState;
- // Don't reset the updateQueue, in case there are pending updates. Resetting
- // is handled by processUpdateQueue.
- }
+// TODO: Delete memoizeProps/State and move to reconcile/bailout instead
+function memoizeProps(workInProgress: Fiber, nextProps: any) {
+ workInProgress.memoizedProps = nextProps;
+}
- function beginWork(
- current: Fiber | null,
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ): Fiber | null {
- if (
- workInProgress.expirationTime === NoWork ||
- workInProgress.expirationTime > renderExpirationTime
- ) {
- return bailoutOnLowPriority(current, workInProgress);
- }
+function memoizeState(workInProgress: Fiber, nextState: any) {
+ workInProgress.memoizedState = nextState;
+ // Don't reset the updateQueue, in case there are pending updates. Resetting
+ // is handled by processUpdateQueue.
+}
- switch (workInProgress.tag) {
- case IndeterminateComponent:
- return mountIndeterminateComponent(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case FunctionalComponent:
- return updateFunctionalComponent(current, workInProgress);
- case ClassComponent:
- return updateClassComponent(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case HostRoot:
- return updateHostRoot(current, workInProgress, renderExpirationTime);
- case HostComponent:
- return updateHostComponent(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case HostText:
- return updateHostText(current, workInProgress);
- case TimeoutComponent:
- return updateTimeoutComponent(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case HostPortal:
- return updatePortalComponent(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case ForwardRef:
- return updateForwardRef(current, workInProgress);
- case Fragment:
- return updateFragment(current, workInProgress);
- case Mode:
- return updateMode(current, workInProgress);
- case Profiler:
- return updateProfiler(current, workInProgress);
- case ContextProvider:
- return updateContextProvider(
- current,
- workInProgress,
- renderExpirationTime,
- );
- case ContextConsumer:
- return updateContextConsumer(
- current,
- workInProgress,
- renderExpirationTime,
- );
- default:
- invariant(
- false,
- 'Unknown unit of work tag. This error is likely caused by a bug in ' +
- 'React. Please file an issue.',
- );
- }
+function beginWork(
+ current: Fiber | null,
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+): Fiber | null {
+ if (
+ workInProgress.expirationTime === NoWork ||
+ workInProgress.expirationTime > renderExpirationTime
+ ) {
+ return bailoutOnLowPriority(current, workInProgress);
}
- return {
- beginWork,
- };
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ return mountIndeterminateComponent(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ case FunctionalComponent:
+ return updateFunctionalComponent(current, workInProgress);
+ case ClassComponent:
+ return updateClassComponent(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ case HostRoot:
+ return updateHostRoot(current, workInProgress, renderExpirationTime);
+ case HostComponent:
+ return updateHostComponent(current, workInProgress, renderExpirationTime);
+ case HostText:
+ return updateHostText(current, workInProgress);
+ case TimeoutComponent:
+ return updateTimeoutComponent(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ case HostPortal:
+ return updatePortalComponent(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ case ForwardRef:
+ return updateForwardRef(current, workInProgress);
+ case Fragment:
+ return updateFragment(current, workInProgress);
+ case Mode:
+ return updateMode(current, workInProgress);
+ case Profiler:
+ return updateProfiler(current, workInProgress);
+ case ContextProvider:
+ return updateContextProvider(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ case ContextConsumer:
+ return updateContextConsumer(
+ current,
+ workInProgress,
+ renderExpirationTime,
+ );
+ default:
+ invariant(
+ false,
+ 'Unknown unit of work tag. This error is likely caused by a bug in ' +
+ 'React. Please file an issue.',
+ );
+ }
}
+
+export {beginWork};
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 0a0cebddae9cb..af1f409aa2d3c 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -9,7 +9,6 @@
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
-import type {LegacyContext} from './ReactFiberContext';
import {Update, Snapshot} from 'shared/ReactTypeOfSideEffect';
import {
@@ -39,6 +38,18 @@ import {
ForceUpdate,
} from './ReactUpdateQueue';
import {NoWork} from './ReactFiberExpirationTime';
+import {
+ cacheContext,
+ getMaskedContext,
+ getUnmaskedContext,
+ isContextConsumer,
+ hasContextChanged,
+} from './ReactFiberContext';
+import {
+ recalculateCurrentTime,
+ computeExpirationForFiber,
+ scheduleWork,
+} from './ReactFiberScheduler';
const fakeInternalInstance = {};
const isArray = Array.isArray;
@@ -152,545 +163,551 @@ export function applyDerivedStateFromProps(
}
}
-export default function(
- legacyContext: LegacyContext,
- scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
- computeExpirationForFiber: (
- currentTime: ExpirationTime,
- fiber: Fiber,
- ) => ExpirationTime,
- memoizeProps: (workInProgress: Fiber, props: any) => void,
- memoizeState: (workInProgress: Fiber, state: any) => void,
- recalculateCurrentTime: () => ExpirationTime,
-) {
- const {
- cacheContext,
- getMaskedContext,
- getUnmaskedContext,
- isContextConsumer,
- hasContextChanged,
- } = legacyContext;
-
- const classComponentUpdater = {
- isMounted,
- enqueueSetState(inst, payload, callback) {
- const fiber = ReactInstanceMap.get(inst);
- const currentTime = recalculateCurrentTime();
- const expirationTime = computeExpirationForFiber(currentTime, fiber);
-
- const update = createUpdate(expirationTime);
- update.payload = payload;
- if (callback !== undefined && callback !== null) {
- if (__DEV__) {
- warnOnInvalidCallback(callback, 'setState');
- }
- update.callback = callback;
- }
+const classComponentUpdater = {
+ isMounted,
+ enqueueSetState(inst, payload, callback) {
+ const fiber = ReactInstanceMap.get(inst);
+ const currentTime = recalculateCurrentTime();
+ const expirationTime = computeExpirationForFiber(currentTime, fiber);
- enqueueUpdate(fiber, update, expirationTime);
- scheduleWork(fiber, expirationTime);
- },
- enqueueReplaceState(inst, payload, callback) {
- const fiber = ReactInstanceMap.get(inst);
- const currentTime = recalculateCurrentTime();
- const expirationTime = computeExpirationForFiber(currentTime, fiber);
-
- const update = createUpdate(expirationTime);
- update.tag = ReplaceState;
- update.payload = payload;
-
- if (callback !== undefined && callback !== null) {
- if (__DEV__) {
- warnOnInvalidCallback(callback, 'replaceState');
- }
- update.callback = callback;
+ const update = createUpdate(expirationTime);
+ update.payload = payload;
+ if (callback !== undefined && callback !== null) {
+ if (__DEV__) {
+ warnOnInvalidCallback(callback, 'setState');
}
+ update.callback = callback;
+ }
- enqueueUpdate(fiber, update, expirationTime);
- scheduleWork(fiber, expirationTime);
- },
- enqueueForceUpdate(inst, callback) {
- const fiber = ReactInstanceMap.get(inst);
- const currentTime = recalculateCurrentTime();
- const expirationTime = computeExpirationForFiber(currentTime, fiber);
+ enqueueUpdate(fiber, update, expirationTime);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueReplaceState(inst, payload, callback) {
+ const fiber = ReactInstanceMap.get(inst);
+ const currentTime = recalculateCurrentTime();
+ const expirationTime = computeExpirationForFiber(currentTime, fiber);
- const update = createUpdate(expirationTime);
- update.tag = ForceUpdate;
+ const update = createUpdate(expirationTime);
+ update.tag = ReplaceState;
+ update.payload = payload;
- if (callback !== undefined && callback !== null) {
- if (__DEV__) {
- warnOnInvalidCallback(callback, 'forceUpdate');
- }
- update.callback = callback;
+ if (callback !== undefined && callback !== null) {
+ if (__DEV__) {
+ warnOnInvalidCallback(callback, 'replaceState');
}
+ update.callback = callback;
+ }
- enqueueUpdate(fiber, update, expirationTime);
- scheduleWork(fiber, expirationTime);
- },
- };
+ enqueueUpdate(fiber, update, expirationTime);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueForceUpdate(inst, callback) {
+ const fiber = ReactInstanceMap.get(inst);
+ const currentTime = recalculateCurrentTime();
+ const expirationTime = computeExpirationForFiber(currentTime, fiber);
- function checkShouldComponentUpdate(
- workInProgress,
- oldProps,
- newProps,
- oldState,
- newState,
- newContext,
- ) {
- const instance = workInProgress.stateNode;
- const ctor = workInProgress.type;
- if (typeof instance.shouldComponentUpdate === 'function') {
- startPhaseTimer(workInProgress, 'shouldComponentUpdate');
- const shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- newContext,
- );
- stopPhaseTimer();
+ const update = createUpdate(expirationTime);
+ update.tag = ForceUpdate;
+ if (callback !== undefined && callback !== null) {
if (__DEV__) {
- warning(
- shouldUpdate !== undefined,
- '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
- 'boolean value. Make sure to return true or false.',
- getComponentName(workInProgress) || 'Component',
- );
+ warnOnInvalidCallback(callback, 'forceUpdate');
}
-
- return shouldUpdate;
+ update.callback = callback;
}
- if (ctor.prototype && ctor.prototype.isPureReactComponent) {
- return (
- !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
+ enqueueUpdate(fiber, update, expirationTime);
+ scheduleWork(fiber, expirationTime);
+ },
+};
+
+function checkShouldComponentUpdate(
+ workInProgress,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ newContext,
+) {
+ const instance = workInProgress.stateNode;
+ const ctor = workInProgress.type;
+ if (typeof instance.shouldComponentUpdate === 'function') {
+ startPhaseTimer(workInProgress, 'shouldComponentUpdate');
+ const shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ newContext,
+ );
+ stopPhaseTimer();
+
+ if (__DEV__) {
+ warning(
+ shouldUpdate !== undefined,
+ '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
+ 'boolean value. Make sure to return true or false.',
+ getComponentName(workInProgress) || 'Component',
);
}
- return true;
+ return shouldUpdate;
}
- function checkClassInstance(workInProgress: Fiber) {
- const instance = workInProgress.stateNode;
- const type = workInProgress.type;
- if (__DEV__) {
- const name = getComponentName(workInProgress) || 'Component';
- const renderPresent = instance.render;
+ if (ctor.prototype && ctor.prototype.isPureReactComponent) {
+ return (
+ !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
+ );
+ }
- if (!renderPresent) {
- if (type.prototype && typeof type.prototype.render === 'function') {
- warning(
- false,
- '%s(...): No `render` method found on the returned component ' +
- 'instance: did you accidentally return an object from the constructor?',
- name,
- );
- } else {
- warning(
- false,
- '%s(...): No `render` method found on the returned component ' +
- 'instance: you may have forgotten to define `render`.',
- name,
- );
- }
- }
+ return true;
+}
- const noGetInitialStateOnES6 =
- !instance.getInitialState ||
- instance.getInitialState.isReactClassApproved ||
- instance.state;
- warning(
- noGetInitialStateOnES6,
- 'getInitialState was defined on %s, a plain JavaScript class. ' +
- 'This is only supported for classes created using React.createClass. ' +
- 'Did you mean to define a state property instead?',
- name,
- );
- const noGetDefaultPropsOnES6 =
- !instance.getDefaultProps ||
- instance.getDefaultProps.isReactClassApproved;
- warning(
- noGetDefaultPropsOnES6,
- 'getDefaultProps was defined on %s, a plain JavaScript class. ' +
- 'This is only supported for classes created using React.createClass. ' +
- 'Use a static property to define defaultProps instead.',
- name,
- );
- const noInstancePropTypes = !instance.propTypes;
- warning(
- noInstancePropTypes,
- 'propTypes was defined as an instance property on %s. Use a static ' +
- 'property to define propTypes instead.',
- name,
- );
- const noInstanceContextTypes = !instance.contextTypes;
- warning(
- noInstanceContextTypes,
- 'contextTypes was defined as an instance property on %s. Use a static ' +
- 'property to define contextTypes instead.',
- name,
- );
- const noComponentShouldUpdate =
- typeof instance.componentShouldUpdate !== 'function';
- warning(
- noComponentShouldUpdate,
- '%s has a method called ' +
- 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
- 'The name is phrased as a question because the function is ' +
- 'expected to return a value.',
- name,
- );
- if (
- type.prototype &&
- type.prototype.isPureReactComponent &&
- typeof instance.shouldComponentUpdate !== 'undefined'
- ) {
+function checkClassInstance(workInProgress: Fiber) {
+ const instance = workInProgress.stateNode;
+ const type = workInProgress.type;
+ if (__DEV__) {
+ const name = getComponentName(workInProgress) || 'Component';
+ const renderPresent = instance.render;
+
+ if (!renderPresent) {
+ if (type.prototype && typeof type.prototype.render === 'function') {
warning(
false,
- '%s has a method called shouldComponentUpdate(). ' +
- 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
- 'Please extend React.Component if shouldComponentUpdate is used.',
- getComponentName(workInProgress) || 'A pure component',
+ '%s(...): No `render` method found on the returned component ' +
+ 'instance: did you accidentally return an object from the constructor?',
+ name,
);
- }
- const noComponentDidUnmount =
- typeof instance.componentDidUnmount !== 'function';
- warning(
- noComponentDidUnmount,
- '%s has a method called ' +
- 'componentDidUnmount(). But there is no such lifecycle method. ' +
- 'Did you mean componentWillUnmount()?',
- name,
- );
- const noComponentDidReceiveProps =
- typeof instance.componentDidReceiveProps !== 'function';
- warning(
- noComponentDidReceiveProps,
- '%s has a method called ' +
- 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
- 'If you meant to update the state in response to changing props, ' +
- 'use componentWillReceiveProps(). If you meant to fetch data or ' +
- 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
- name,
- );
- const noComponentWillRecieveProps =
- typeof instance.componentWillRecieveProps !== 'function';
- warning(
- noComponentWillRecieveProps,
- '%s has a method called ' +
- 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
- name,
- );
- const noUnsafeComponentWillRecieveProps =
- typeof instance.UNSAFE_componentWillRecieveProps !== 'function';
- warning(
- noUnsafeComponentWillRecieveProps,
- '%s has a method called ' +
- 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
- name,
- );
- const hasMutatedProps = instance.props !== workInProgress.pendingProps;
- warning(
- instance.props === undefined || !hasMutatedProps,
- '%s(...): When calling super() in `%s`, make sure to pass ' +
- "up the same props that your component's constructor was passed.",
- name,
- name,
- );
- const noInstanceDefaultProps = !instance.defaultProps;
- warning(
- noInstanceDefaultProps,
- 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
- ' Instead, define defaultProps as a static property on %s.',
- name,
- name,
- );
-
- if (
- typeof instance.getSnapshotBeforeUpdate === 'function' &&
- typeof instance.componentDidUpdate !== 'function' &&
- !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(type)
- ) {
- didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(type);
+ } else {
warning(
false,
- '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
- 'This component defines getSnapshotBeforeUpdate() only.',
- getComponentName(workInProgress),
+ '%s(...): No `render` method found on the returned component ' +
+ 'instance: you may have forgotten to define `render`.',
+ name,
);
}
+ }
- const noInstanceGetDerivedStateFromProps =
- typeof instance.getDerivedStateFromProps !== 'function';
+ const noGetInitialStateOnES6 =
+ !instance.getInitialState ||
+ instance.getInitialState.isReactClassApproved ||
+ instance.state;
+ warning(
+ noGetInitialStateOnES6,
+ 'getInitialState was defined on %s, a plain JavaScript class. ' +
+ 'This is only supported for classes created using React.createClass. ' +
+ 'Did you mean to define a state property instead?',
+ name,
+ );
+ const noGetDefaultPropsOnES6 =
+ !instance.getDefaultProps ||
+ instance.getDefaultProps.isReactClassApproved;
+ warning(
+ noGetDefaultPropsOnES6,
+ 'getDefaultProps was defined on %s, a plain JavaScript class. ' +
+ 'This is only supported for classes created using React.createClass. ' +
+ 'Use a static property to define defaultProps instead.',
+ name,
+ );
+ const noInstancePropTypes = !instance.propTypes;
+ warning(
+ noInstancePropTypes,
+ 'propTypes was defined as an instance property on %s. Use a static ' +
+ 'property to define propTypes instead.',
+ name,
+ );
+ const noInstanceContextTypes = !instance.contextTypes;
+ warning(
+ noInstanceContextTypes,
+ 'contextTypes was defined as an instance property on %s. Use a static ' +
+ 'property to define contextTypes instead.',
+ name,
+ );
+ const noComponentShouldUpdate =
+ typeof instance.componentShouldUpdate !== 'function';
+ warning(
+ noComponentShouldUpdate,
+ '%s has a method called ' +
+ 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
+ 'The name is phrased as a question because the function is ' +
+ 'expected to return a value.',
+ name,
+ );
+ if (
+ type.prototype &&
+ type.prototype.isPureReactComponent &&
+ typeof instance.shouldComponentUpdate !== 'undefined'
+ ) {
warning(
- noInstanceGetDerivedStateFromProps,
- '%s: getDerivedStateFromProps() is defined as an instance method ' +
- 'and will be ignored. Instead, declare it as a static method.',
- name,
+ false,
+ '%s has a method called shouldComponentUpdate(). ' +
+ 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
+ 'Please extend React.Component if shouldComponentUpdate is used.',
+ getComponentName(workInProgress) || 'A pure component',
);
- const noInstanceGetDerivedStateFromCatch =
- typeof instance.getDerivedStateFromCatch !== 'function';
+ }
+ const noComponentDidUnmount =
+ typeof instance.componentDidUnmount !== 'function';
+ warning(
+ noComponentDidUnmount,
+ '%s has a method called ' +
+ 'componentDidUnmount(). But there is no such lifecycle method. ' +
+ 'Did you mean componentWillUnmount()?',
+ name,
+ );
+ const noComponentDidReceiveProps =
+ typeof instance.componentDidReceiveProps !== 'function';
+ warning(
+ noComponentDidReceiveProps,
+ '%s has a method called ' +
+ 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
+ 'If you meant to update the state in response to changing props, ' +
+ 'use componentWillReceiveProps(). If you meant to fetch data or ' +
+ 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
+ name,
+ );
+ const noComponentWillRecieveProps =
+ typeof instance.componentWillRecieveProps !== 'function';
+ warning(
+ noComponentWillRecieveProps,
+ '%s has a method called ' +
+ 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
+ name,
+ );
+ const noUnsafeComponentWillRecieveProps =
+ typeof instance.UNSAFE_componentWillRecieveProps !== 'function';
+ warning(
+ noUnsafeComponentWillRecieveProps,
+ '%s has a method called ' +
+ 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
+ name,
+ );
+ const hasMutatedProps = instance.props !== workInProgress.pendingProps;
+ warning(
+ instance.props === undefined || !hasMutatedProps,
+ '%s(...): When calling super() in `%s`, make sure to pass ' +
+ "up the same props that your component's constructor was passed.",
+ name,
+ name,
+ );
+ const noInstanceDefaultProps = !instance.defaultProps;
+ warning(
+ noInstanceDefaultProps,
+ 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
+ ' Instead, define defaultProps as a static property on %s.',
+ name,
+ name,
+ );
+
+ if (
+ typeof instance.getSnapshotBeforeUpdate === 'function' &&
+ typeof instance.componentDidUpdate !== 'function' &&
+ !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(type)
+ ) {
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(type);
warning(
- noInstanceGetDerivedStateFromCatch,
- '%s: getDerivedStateFromCatch() is defined as an instance method ' +
- 'and will be ignored. Instead, declare it as a static method.',
- name,
+ false,
+ '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
+ 'This component defines getSnapshotBeforeUpdate() only.',
+ getComponentName(workInProgress),
);
- const noStaticGetSnapshotBeforeUpdate =
- typeof type.getSnapshotBeforeUpdate !== 'function';
+ }
+
+ const noInstanceGetDerivedStateFromProps =
+ typeof instance.getDerivedStateFromProps !== 'function';
+ warning(
+ noInstanceGetDerivedStateFromProps,
+ '%s: getDerivedStateFromProps() is defined as an instance method ' +
+ 'and will be ignored. Instead, declare it as a static method.',
+ name,
+ );
+ const noInstanceGetDerivedStateFromCatch =
+ typeof instance.getDerivedStateFromCatch !== 'function';
+ warning(
+ noInstanceGetDerivedStateFromCatch,
+ '%s: getDerivedStateFromCatch() is defined as an instance method ' +
+ 'and will be ignored. Instead, declare it as a static method.',
+ name,
+ );
+ const noStaticGetSnapshotBeforeUpdate =
+ typeof type.getSnapshotBeforeUpdate !== 'function';
+ warning(
+ noStaticGetSnapshotBeforeUpdate,
+ '%s: getSnapshotBeforeUpdate() is defined as a static method ' +
+ 'and will be ignored. Instead, declare it as an instance method.',
+ name,
+ );
+ const state = instance.state;
+ if (state && (typeof state !== 'object' || isArray(state))) {
+ warning(false, '%s.state: must be set to an object or null', name);
+ }
+ if (typeof instance.getChildContext === 'function') {
warning(
- noStaticGetSnapshotBeforeUpdate,
- '%s: getSnapshotBeforeUpdate() is defined as a static method ' +
- 'and will be ignored. Instead, declare it as an instance method.',
+ typeof type.childContextTypes === 'object',
+ '%s.getChildContext(): childContextTypes must be defined in order to ' +
+ 'use getChildContext().',
name,
);
- const state = instance.state;
- if (state && (typeof state !== 'object' || isArray(state))) {
- warning(false, '%s.state: must be set to an object or null', name);
- }
- if (typeof instance.getChildContext === 'function') {
- warning(
- typeof type.childContextTypes === 'object',
- '%s.getChildContext(): childContextTypes must be defined in order to ' +
- 'use getChildContext().',
- name,
- );
- }
}
}
+}
- function adoptClassInstance(workInProgress: Fiber, instance: any): void {
- instance.updater = classComponentUpdater;
- workInProgress.stateNode = instance;
- // The instance needs access to the fiber so that it can schedule updates
- ReactInstanceMap.set(instance, workInProgress);
- if (__DEV__) {
- instance._reactInternalInstance = fakeInternalInstance;
+function adoptClassInstance(workInProgress: Fiber, instance: any): void {
+ instance.updater = classComponentUpdater;
+ workInProgress.stateNode = instance;
+ // The instance needs access to the fiber so that it can schedule updates
+ ReactInstanceMap.set(instance, workInProgress);
+ if (__DEV__) {
+ instance._reactInternalInstance = fakeInternalInstance;
+ }
+}
+
+function constructClassInstance(
+ workInProgress: Fiber,
+ props: any,
+ renderExpirationTime: ExpirationTime,
+): any {
+ const ctor = workInProgress.type;
+ const unmaskedContext = getUnmaskedContext(workInProgress);
+ const needsContext = isContextConsumer(workInProgress);
+ const context = needsContext
+ ? getMaskedContext(workInProgress, unmaskedContext)
+ : emptyObject;
+
+ // Instantiate twice to help detect side-effects.
+ if (__DEV__) {
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.mode & StrictMode)
+ ) {
+ new ctor(props, context); // eslint-disable-line no-new
}
}
- function constructClassInstance(
- workInProgress: Fiber,
- props: any,
- renderExpirationTime: ExpirationTime,
- ): any {
- const ctor = workInProgress.type;
- const unmaskedContext = getUnmaskedContext(workInProgress);
- const needsContext = isContextConsumer(workInProgress);
- const context = needsContext
- ? getMaskedContext(workInProgress, unmaskedContext)
- : emptyObject;
-
- // Instantiate twice to help detect side-effects.
- if (__DEV__) {
- if (
- debugRenderPhaseSideEffects ||
- (debugRenderPhaseSideEffectsForStrictMode &&
- workInProgress.mode & StrictMode)
- ) {
- new ctor(props, context); // eslint-disable-line no-new
+ const instance = new ctor(props, context);
+ const state = (workInProgress.memoizedState =
+ instance.state !== null && instance.state !== undefined
+ ? instance.state
+ : null);
+ adoptClassInstance(workInProgress, instance);
+
+ if (__DEV__) {
+ if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
+ const componentName = getComponentName(workInProgress) || 'Component';
+ if (!didWarnAboutUninitializedState.has(componentName)) {
+ didWarnAboutUninitializedState.add(componentName);
+ warning(
+ false,
+ '%s: Did not properly initialize state during construction. ' +
+ 'Expected state to be an object, but it was %s.',
+ componentName,
+ instance.state === null ? 'null' : 'undefined',
+ );
}
}
- const instance = new ctor(props, context);
- const state = (workInProgress.memoizedState =
- instance.state !== null && instance.state !== undefined
- ? instance.state
- : null);
- adoptClassInstance(workInProgress, instance);
-
- if (__DEV__) {
+ // If new component APIs are defined, "unsafe" lifecycles won't be called.
+ // Warn about these lifecycles if they are present.
+ // Don't warn about react-lifecycles-compat polyfilled methods though.
+ if (
+ typeof ctor.getDerivedStateFromProps === 'function' ||
+ typeof instance.getSnapshotBeforeUpdate === 'function'
+ ) {
+ let foundWillMountName = null;
+ let foundWillReceivePropsName = null;
+ let foundWillUpdateName = null;
+ if (
+ typeof instance.componentWillMount === 'function' &&
+ instance.componentWillMount.__suppressDeprecationWarning !== true
+ ) {
+ foundWillMountName = 'componentWillMount';
+ } else if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ foundWillMountName = 'UNSAFE_componentWillMount';
+ }
+ if (
+ typeof instance.componentWillReceiveProps === 'function' &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
+ ) {
+ foundWillReceivePropsName = 'componentWillReceiveProps';
+ } else if (
+ typeof instance.UNSAFE_componentWillReceiveProps === 'function'
+ ) {
+ foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
+ }
if (
- typeof ctor.getDerivedStateFromProps === 'function' &&
- state === null
+ typeof instance.componentWillUpdate === 'function' &&
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
+ ) {
+ foundWillUpdateName = 'componentWillUpdate';
+ } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ foundWillUpdateName = 'UNSAFE_componentWillUpdate';
+ }
+ if (
+ foundWillMountName !== null ||
+ foundWillReceivePropsName !== null ||
+ foundWillUpdateName !== null
) {
const componentName = getComponentName(workInProgress) || 'Component';
- if (!didWarnAboutUninitializedState.has(componentName)) {
- didWarnAboutUninitializedState.add(componentName);
+ const newApiName =
+ typeof ctor.getDerivedStateFromProps === 'function'
+ ? 'getDerivedStateFromProps()'
+ : 'getSnapshotBeforeUpdate()';
+ if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
+ didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
warning(
false,
- '%s: Did not properly initialize state during construction. ' +
- 'Expected state to be an object, but it was %s.',
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
+ '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
+ 'https://fb.me/react-async-component-lifecycle-hooks',
componentName,
- instance.state === null ? 'null' : 'undefined',
+ newApiName,
+ foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
+ foundWillReceivePropsName !== null
+ ? `\n ${foundWillReceivePropsName}`
+ : '',
+ foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
);
}
}
-
- // If new component APIs are defined, "unsafe" lifecycles won't be called.
- // Warn about these lifecycles if they are present.
- // Don't warn about react-lifecycles-compat polyfilled methods though.
- if (
- typeof ctor.getDerivedStateFromProps === 'function' ||
- typeof instance.getSnapshotBeforeUpdate === 'function'
- ) {
- let foundWillMountName = null;
- let foundWillReceivePropsName = null;
- let foundWillUpdateName = null;
- if (
- typeof instance.componentWillMount === 'function' &&
- instance.componentWillMount.__suppressDeprecationWarning !== true
- ) {
- foundWillMountName = 'componentWillMount';
- } else if (typeof instance.UNSAFE_componentWillMount === 'function') {
- foundWillMountName = 'UNSAFE_componentWillMount';
- }
- if (
- typeof instance.componentWillReceiveProps === 'function' &&
- instance.componentWillReceiveProps.__suppressDeprecationWarning !==
- true
- ) {
- foundWillReceivePropsName = 'componentWillReceiveProps';
- } else if (
- typeof instance.UNSAFE_componentWillReceiveProps === 'function'
- ) {
- foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
- }
- if (
- typeof instance.componentWillUpdate === 'function' &&
- instance.componentWillUpdate.__suppressDeprecationWarning !== true
- ) {
- foundWillUpdateName = 'componentWillUpdate';
- } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
- foundWillUpdateName = 'UNSAFE_componentWillUpdate';
- }
- if (
- foundWillMountName !== null ||
- foundWillReceivePropsName !== null ||
- foundWillUpdateName !== null
- ) {
- const componentName = getComponentName(workInProgress) || 'Component';
- const newApiName =
- typeof ctor.getDerivedStateFromProps === 'function'
- ? 'getDerivedStateFromProps()'
- : 'getSnapshotBeforeUpdate()';
- if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
- didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
- warning(
- false,
- 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
- '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
- 'The above lifecycles should be removed. Learn more about this warning here:\n' +
- 'https://fb.me/react-async-component-lifecycle-hooks',
- componentName,
- newApiName,
- foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
- foundWillReceivePropsName !== null
- ? `\n ${foundWillReceivePropsName}`
- : '',
- foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
- );
- }
- }
- }
}
+ }
- // Cache unmasked context so we can avoid recreating masked context unless necessary.
- // ReactFiberContext usually updates this cache but can't for newly-created instances.
- if (needsContext) {
- cacheContext(workInProgress, unmaskedContext, context);
- }
+ // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // ReactFiberContext usually updates this cache but can't for newly-created instances.
+ if (needsContext) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return instance;
+}
- return instance;
+function callComponentWillMount(workInProgress, instance) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ const oldState = instance.state;
+
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
}
- function callComponentWillMount(workInProgress, instance) {
- startPhaseTimer(workInProgress, 'componentWillMount');
- const oldState = instance.state;
+ stopPhaseTimer();
- if (typeof instance.componentWillMount === 'function') {
- instance.componentWillMount();
- }
- if (typeof instance.UNSAFE_componentWillMount === 'function') {
- instance.UNSAFE_componentWillMount();
+ if (oldState !== instance.state) {
+ if (__DEV__) {
+ warning(
+ false,
+ '%s.componentWillMount(): Assigning directly to this.state is ' +
+ "deprecated (except inside a component's " +
+ 'constructor). Use setState instead.',
+ getComponentName(workInProgress) || 'Component',
+ );
}
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
- stopPhaseTimer();
+function callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ newContext,
+) {
+ const oldState = instance.state;
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
+ if (typeof instance.componentWillReceiveProps === 'function') {
+ instance.componentWillReceiveProps(newProps, newContext);
+ }
+ if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
+ }
+ stopPhaseTimer();
- if (oldState !== instance.state) {
- if (__DEV__) {
+ if (instance.state !== oldState) {
+ if (__DEV__) {
+ const componentName = getComponentName(workInProgress) || 'Component';
+ if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
+ didWarnAboutStateAssignmentForComponent.add(componentName);
warning(
false,
- '%s.componentWillMount(): Assigning directly to this.state is ' +
- "deprecated (except inside a component's " +
+ '%s.componentWillReceiveProps(): Assigning directly to ' +
+ "this.state is deprecated (except inside a component's " +
'constructor). Use setState instead.',
- getComponentName(workInProgress) || 'Component',
+ componentName,
);
}
- classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
+}
- function callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- newContext,
- ) {
- const oldState = instance.state;
- startPhaseTimer(workInProgress, 'componentWillReceiveProps');
- if (typeof instance.componentWillReceiveProps === 'function') {
- instance.componentWillReceiveProps(newProps, newContext);
- }
- if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
- instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
- }
- stopPhaseTimer();
+// Invokes the mount life-cycles on a previously never rendered instance.
+function mountClassInstance(
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+): void {
+ const ctor = workInProgress.type;
- if (instance.state !== oldState) {
- if (__DEV__) {
- const componentName = getComponentName(workInProgress) || 'Component';
- if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
- didWarnAboutStateAssignmentForComponent.add(componentName);
- warning(
- false,
- '%s.componentWillReceiveProps(): Assigning directly to ' +
- "this.state is deprecated (except inside a component's " +
- 'constructor). Use setState instead.',
- componentName,
- );
- }
- }
- classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
- }
+ if (__DEV__) {
+ checkClassInstance(workInProgress);
}
- // Invokes the mount life-cycles on a previously never rendered instance.
- function mountClassInstance(
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ): void {
- const ctor = workInProgress.type;
+ const instance = workInProgress.stateNode;
+ const props = workInProgress.pendingProps;
+ const unmaskedContext = getUnmaskedContext(workInProgress);
- if (__DEV__) {
- checkClassInstance(workInProgress);
+ instance.props = props;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = emptyObject;
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
+
+ if (__DEV__) {
+ if (workInProgress.mode & StrictMode) {
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
+ workInProgress,
+ instance,
+ );
}
- const instance = workInProgress.stateNode;
- const props = workInProgress.pendingProps;
- const unmaskedContext = getUnmaskedContext(workInProgress);
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.recordDeprecationWarnings(
+ workInProgress,
+ instance,
+ );
+ }
+ }
- instance.props = props;
+ let updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(
+ workInProgress,
+ updateQueue,
+ props,
+ instance,
+ renderExpirationTime,
+ );
instance.state = workInProgress.memoizedState;
- instance.refs = emptyObject;
- instance.context = getMaskedContext(workInProgress, unmaskedContext);
-
- if (__DEV__) {
- if (workInProgress.mode & StrictMode) {
- ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
- workInProgress,
- instance,
- );
- }
+ }
- if (warnAboutDeprecatedLifecycles) {
- ReactStrictModeWarnings.recordDeprecationWarnings(
- workInProgress,
- instance,
- );
- }
- }
+ const getDerivedStateFromProps = workInProgress.type.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, props);
+ instance.state = workInProgress.memoizedState;
+ }
- let updateQueue = workInProgress.updateQueue;
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (
+ typeof ctor.getDerivedStateFromProps !== 'function' &&
+ typeof instance.getSnapshotBeforeUpdate !== 'function' &&
+ (typeof instance.UNSAFE_componentWillMount === 'function' ||
+ typeof instance.componentWillMount === 'function')
+ ) {
+ callComponentWillMount(workInProgress, instance);
+ // If we had additional state updates during this life-cycle, let's
+ // process them now.
+ updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(
workInProgress,
@@ -701,350 +718,315 @@ export default function(
);
instance.state = workInProgress.memoizedState;
}
+ }
- const getDerivedStateFromProps =
- workInProgress.type.getDerivedStateFromProps;
- if (typeof getDerivedStateFromProps === 'function') {
- applyDerivedStateFromProps(
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+}
+
+function resumeMountClassInstance(
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+): boolean {
+ const ctor = workInProgress.type;
+ const instance = workInProgress.stateNode;
+
+ const oldProps = workInProgress.memoizedProps;
+ const newProps = workInProgress.pendingProps;
+ instance.props = oldProps;
+
+ const oldContext = instance.context;
+ const newUnmaskedContext = getUnmaskedContext(workInProgress);
+ const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
+
+ const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ const hasNewLifecycles =
+ typeof getDerivedStateFromProps === 'function' ||
+ typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
+ typeof instance.componentWillReceiveProps === 'function')
+ ) {
+ if (oldProps !== newProps || oldContext !== newContext) {
+ callComponentWillReceiveProps(
workInProgress,
- getDerivedStateFromProps,
- props,
+ instance,
+ newProps,
+ newContext,
);
- instance.state = workInProgress.memoizedState;
- }
-
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
- if (
- typeof ctor.getDerivedStateFromProps !== 'function' &&
- typeof instance.getSnapshotBeforeUpdate !== 'function' &&
- (typeof instance.UNSAFE_componentWillMount === 'function' ||
- typeof instance.componentWillMount === 'function')
- ) {
- callComponentWillMount(workInProgress, instance);
- // If we had additional state updates during this life-cycle, let's
- // process them now.
- updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- processUpdateQueue(
- workInProgress,
- updateQueue,
- props,
- instance,
- renderExpirationTime,
- );
- instance.state = workInProgress.memoizedState;
- }
}
+ }
+ resetHasForceUpdateBeforeProcessing();
+
+ const oldState = workInProgress.memoizedState;
+ let newState = (instance.state = oldState);
+ let updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(
+ workInProgress,
+ updateQueue,
+ newProps,
+ instance,
+ renderExpirationTime,
+ );
+ newState = workInProgress.memoizedState;
+ }
+ if (
+ oldProps === newProps &&
+ oldState === newState &&
+ !hasContextChanged() &&
+ !checkHasForceUpdateAfterProcessing()
+ ) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
+ return false;
}
- function resumeMountClassInstance(
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ): boolean {
- const ctor = workInProgress.type;
- const instance = workInProgress.stateNode;
-
- const oldProps = workInProgress.memoizedProps;
- const newProps = workInProgress.pendingProps;
- instance.props = oldProps;
-
- const oldContext = instance.context;
- const newUnmaskedContext = getUnmaskedContext(workInProgress);
- const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
-
- const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
- const hasNewLifecycles =
- typeof getDerivedStateFromProps === 'function' ||
- typeof instance.getSnapshotBeforeUpdate === 'function';
-
- // Note: During these life-cycles, instance.props/instance.state are what
- // ever the previously attempted to render - not the "current". However,
- // during componentDidUpdate we pass the "current" props.
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(
+ workInProgress,
+ getDerivedStateFromProps,
+ newProps,
+ );
+ newState = workInProgress.memoizedState;
+ }
+ const shouldUpdate =
+ checkHasForceUpdateAfterProcessing() ||
+ checkShouldComponentUpdate(
+ workInProgress,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ newContext,
+ );
+
+ if (shouldUpdate) {
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
if (
!hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
- typeof instance.componentWillReceiveProps === 'function')
+ (typeof instance.UNSAFE_componentWillMount === 'function' ||
+ typeof instance.componentWillMount === 'function')
) {
- if (oldProps !== newProps || oldContext !== newContext) {
- callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- newContext,
- );
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
}
+ stopPhaseTimer();
}
-
- resetHasForceUpdateBeforeProcessing();
-
- const oldState = workInProgress.memoizedState;
- let newState = (instance.state = oldState);
- let updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- processUpdateQueue(
- workInProgress,
- updateQueue,
- newProps,
- instance,
- renderExpirationTime,
- );
- newState = workInProgress.memoizedState;
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
}
- if (
- oldProps === newProps &&
- oldState === newState &&
- !hasContextChanged() &&
- !checkHasForceUpdateAfterProcessing()
- ) {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidMount === 'function') {
- workInProgress.effectTag |= Update;
- }
- return false;
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
}
- if (typeof getDerivedStateFromProps === 'function') {
- applyDerivedStateFromProps(
- workInProgress,
- getDerivedStateFromProps,
- newProps,
- );
- newState = workInProgress.memoizedState;
- }
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = newContext;
+
+ return shouldUpdate;
+}
- const shouldUpdate =
- checkHasForceUpdateAfterProcessing() ||
- checkShouldComponentUpdate(
+// Invokes the update life-cycles and returns false if it shouldn't rerender.
+function updateClassInstance(
+ current: Fiber,
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+): boolean {
+ const ctor = workInProgress.type;
+ const instance = workInProgress.stateNode;
+
+ const oldProps = workInProgress.memoizedProps;
+ const newProps = workInProgress.pendingProps;
+ instance.props = oldProps;
+
+ const oldContext = instance.context;
+ const newUnmaskedContext = getUnmaskedContext(workInProgress);
+ const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
+
+ const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ const hasNewLifecycles =
+ typeof getDerivedStateFromProps === 'function' ||
+ typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
+ typeof instance.componentWillReceiveProps === 'function')
+ ) {
+ if (oldProps !== newProps || oldContext !== newContext) {
+ callComponentWillReceiveProps(
workInProgress,
- oldProps,
+ instance,
newProps,
- oldState,
- newState,
newContext,
);
+ }
+ }
- if (shouldUpdate) {
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
+ resetHasForceUpdateBeforeProcessing();
+
+ const oldState = workInProgress.memoizedState;
+ let newState = (instance.state = oldState);
+ let updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(
+ workInProgress,
+ updateQueue,
+ newProps,
+ instance,
+ renderExpirationTime,
+ );
+ newState = workInProgress.memoizedState;
+ }
+
+ if (
+ oldProps === newProps &&
+ oldState === newState &&
+ !hasContextChanged() &&
+ !checkHasForceUpdateAfterProcessing()
+ ) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillMount === 'function' ||
- typeof instance.componentWillMount === 'function')
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
) {
- startPhaseTimer(workInProgress, 'componentWillMount');
- if (typeof instance.componentWillMount === 'function') {
- instance.componentWillMount();
- }
- if (typeof instance.UNSAFE_componentWillMount === 'function') {
- instance.UNSAFE_componentWillMount();
- }
- stopPhaseTimer();
- }
- if (typeof instance.componentDidMount === 'function') {
workInProgress.effectTag |= Update;
}
- } else {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidMount === 'function') {
- workInProgress.effectTag |= Update;
- }
-
- // If shouldComponentUpdate returned false, we should still update the
- // memoized state to indicate that this work can be reused.
- workInProgress.memoizedProps = newProps;
- workInProgress.memoizedState = newState;
}
-
- // Update the existing instance's state, props, and context pointers even
- // if shouldComponentUpdate returns false.
- instance.props = newProps;
- instance.state = newState;
- instance.context = newContext;
-
- return shouldUpdate;
- }
-
- // Invokes the update life-cycles and returns false if it shouldn't rerender.
- function updateClassInstance(
- current: Fiber,
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ): boolean {
- const ctor = workInProgress.type;
- const instance = workInProgress.stateNode;
-
- const oldProps = workInProgress.memoizedProps;
- const newProps = workInProgress.pendingProps;
- instance.props = oldProps;
-
- const oldContext = instance.context;
- const newUnmaskedContext = getUnmaskedContext(workInProgress);
- const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
-
- const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
- const hasNewLifecycles =
- typeof getDerivedStateFromProps === 'function' ||
- typeof instance.getSnapshotBeforeUpdate === 'function';
-
- // Note: During these life-cycles, instance.props/instance.state are what
- // ever the previously attempted to render - not the "current". However,
- // during componentDidUpdate we pass the "current" props.
-
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
- if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
- typeof instance.componentWillReceiveProps === 'function')
- ) {
- if (oldProps !== newProps || oldContext !== newContext) {
- callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- newContext,
- );
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.effectTag |= Snapshot;
}
}
+ return false;
+ }
- resetHasForceUpdateBeforeProcessing();
-
- const oldState = workInProgress.memoizedState;
- let newState = (instance.state = oldState);
- let updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- processUpdateQueue(
+ if (typeof getDerivedStateFromProps === 'function') {
+ if (fireGetDerivedStateFromPropsOnStateUpdates || oldProps !== newProps) {
+ applyDerivedStateFromProps(
workInProgress,
- updateQueue,
+ getDerivedStateFromProps,
newProps,
- instance,
- renderExpirationTime,
);
newState = workInProgress.memoizedState;
}
+ }
+ const shouldUpdate =
+ checkHasForceUpdateAfterProcessing() ||
+ checkShouldComponentUpdate(
+ workInProgress,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ newContext,
+ );
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
if (
- oldProps === newProps &&
- oldState === newState &&
- !hasContextChanged() &&
- !checkHasForceUpdateAfterProcessing()
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
+ typeof instance.componentWillUpdate === 'function')
) {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === 'function') {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Update;
- }
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
+ if (typeof instance.componentWillUpdate === 'function') {
+ instance.componentWillUpdate(newProps, newState, newContext);
}
- if (typeof instance.getSnapshotBeforeUpdate === 'function') {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Snapshot;
- }
+ if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
}
- return false;
+ stopPhaseTimer();
}
-
- if (typeof getDerivedStateFromProps === 'function') {
- if (fireGetDerivedStateFromPropsOnStateUpdates || oldProps !== newProps) {
- applyDerivedStateFromProps(
- workInProgress,
- getDerivedStateFromProps,
- newProps,
- );
- newState = workInProgress.memoizedState;
- }
+ if (typeof instance.componentDidUpdate === 'function') {
+ workInProgress.effectTag |= Update;
}
-
- const shouldUpdate =
- checkHasForceUpdateAfterProcessing() ||
- checkShouldComponentUpdate(
- workInProgress,
- oldProps,
- newProps,
- oldState,
- newState,
- newContext,
- );
-
- if (shouldUpdate) {
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ workInProgress.effectTag |= Snapshot;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
- typeof instance.componentWillUpdate === 'function')
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
) {
- startPhaseTimer(workInProgress, 'componentWillUpdate');
- if (typeof instance.componentWillUpdate === 'function') {
- instance.componentWillUpdate(newProps, newState, newContext);
- }
- if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
- instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
- }
- stopPhaseTimer();
- }
- if (typeof instance.componentDidUpdate === 'function') {
workInProgress.effectTag |= Update;
}
- if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
workInProgress.effectTag |= Snapshot;
}
- } else {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === 'function') {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Update;
- }
- }
- if (typeof instance.getSnapshotBeforeUpdate === 'function') {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Snapshot;
- }
- }
-
- // If shouldComponentUpdate returned false, we should still update the
- // memoized props/state to indicate that this work can be reused.
- workInProgress.memoizedProps = newProps;
- workInProgress.memoizedState = newState;
}
- // Update the existing instance's state, props, and context pointers even
- // if shouldComponentUpdate returns false.
- instance.props = newProps;
- instance.state = newState;
- instance.context = newContext;
-
- return shouldUpdate;
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
}
- return {
- adoptClassInstance,
- constructClassInstance,
- mountClassInstance,
- resumeMountClassInstance,
- updateClassInstance,
- };
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = newContext;
+
+ return shouldUpdate;
}
+
+export {
+ adoptClassInstance,
+ constructClassInstance,
+ mountClassInstance,
+ resumeMountClassInstance,
+ updateClassInstance,
+};
diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js
index e5e2f26402b09..9c492b9b79cde 100644
--- a/packages/react-reconciler/src/ReactFiberCommitWork.js
+++ b/packages/react-reconciler/src/ReactFiberCommitWork.js
@@ -7,18 +7,19 @@
* @flow
*/
-import type {HostConfig} from 'react-reconciler';
+import type {
+ Instance,
+ TextInstance,
+ Container,
+ ChildSet,
+ UpdatePayload,
+} from './ReactFiberHostConfig';
import type {Fiber} from './ReactFiber';
import type {FiberRoot} from './ReactFiberRoot';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {CapturedValue, CapturedError} from './ReactCapturedValue';
-import {
- enableMutatingReconciler,
- enableNoopReconciler,
- enablePersistentReconciler,
- enableProfilerTimer,
-} from 'shared/ReactFeatureFlags';
+import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
import {
ClassComponent,
HostRoot,
@@ -44,6 +45,24 @@ import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
import getComponentName from 'shared/getComponentName';
import {getStackAddendumByWorkInProgressFiber} from 'shared/ReactFiberComponentTreeHook';
import {logCapturedError} from './ReactFiberErrorLogger';
+import {
+ getPublicInstance,
+ supportsMutation,
+ supportsPersistence,
+ commitMount,
+ commitUpdate,
+ resetTextContent,
+ commitTextUpdate,
+ appendChild,
+ appendChildToContainer,
+ insertBefore,
+ insertInContainerBefore,
+ removeChild,
+ removeChildFromContainer,
+ replaceContainerChildren,
+ createContainerChildSet,
+} from './ReactFiberHostConfig';
+import {captureCommitPhaseError} from './ReactFiberScheduler';
const {
invokeGuardedCallback,
@@ -92,779 +111,737 @@ export function logError(boundary: Fiber, errorInfo: CapturedValue) {
}
}
-export default function(
- config: HostConfig,
- captureError: (failedFiber: Fiber, error: mixed) => void,
- scheduleWork: (
- fiber: Fiber,
- startTime: ExpirationTime,
- expirationTime: ExpirationTime,
- ) => void,
- computeExpirationForFiber: (
- startTime: ExpirationTime,
- fiber: Fiber,
- ) => ExpirationTime,
- markLegacyErrorBoundaryAsFailed: (instance: mixed) => void,
- recalculateCurrentTime: () => ExpirationTime,
-) {
- const {getPublicInstance, mutation, persistence} = config;
-
- const callComponentWillUnmountWithTimer = function(current, instance) {
- startPhaseTimer(current, 'componentWillUnmount');
- instance.props = current.memoizedProps;
- instance.state = current.memoizedState;
- instance.componentWillUnmount();
- stopPhaseTimer();
- };
-
- // Capture errors so they don't interrupt unmounting.
- function safelyCallComponentWillUnmount(current, instance) {
- if (__DEV__) {
- invokeGuardedCallback(
- null,
- callComponentWillUnmountWithTimer,
- null,
- current,
- instance,
- );
- if (hasCaughtError()) {
- const unmountError = clearCaughtError();
- captureError(current, unmountError);
- }
- } else {
- try {
- callComponentWillUnmountWithTimer(current, instance);
- } catch (unmountError) {
- captureError(current, unmountError);
- }
+const callComponentWillUnmountWithTimer = function(current, instance) {
+ startPhaseTimer(current, 'componentWillUnmount');
+ instance.props = current.memoizedProps;
+ instance.state = current.memoizedState;
+ instance.componentWillUnmount();
+ stopPhaseTimer();
+};
+
+// Capture errors so they don't interrupt unmounting.
+function safelyCallComponentWillUnmount(current, instance) {
+ if (__DEV__) {
+ invokeGuardedCallback(
+ null,
+ callComponentWillUnmountWithTimer,
+ null,
+ current,
+ instance,
+ );
+ if (hasCaughtError()) {
+ const unmountError = clearCaughtError();
+ captureCommitPhaseError(current, unmountError);
+ }
+ } else {
+ try {
+ callComponentWillUnmountWithTimer(current, instance);
+ } catch (unmountError) {
+ captureCommitPhaseError(current, unmountError);
}
}
+}
- function safelyDetachRef(current: Fiber) {
- const ref = current.ref;
- if (ref !== null) {
- if (typeof ref === 'function') {
- if (__DEV__) {
- invokeGuardedCallback(null, ref, null, null);
- if (hasCaughtError()) {
- const refError = clearCaughtError();
- captureError(current, refError);
- }
- } else {
- try {
- ref(null);
- } catch (refError) {
- captureError(current, refError);
- }
+function safelyDetachRef(current: Fiber) {
+ const ref = current.ref;
+ if (ref !== null) {
+ if (typeof ref === 'function') {
+ if (__DEV__) {
+ invokeGuardedCallback(null, ref, null, null);
+ if (hasCaughtError()) {
+ const refError = clearCaughtError();
+ captureCommitPhaseError(current, refError);
}
} else {
- ref.current = null;
+ try {
+ ref(null);
+ } catch (refError) {
+ captureCommitPhaseError(current, refError);
+ }
}
+ } else {
+ ref.current = null;
}
}
+}
- function commitBeforeMutationLifeCycles(
- current: Fiber | null,
- finishedWork: Fiber,
- ): void {
- switch (finishedWork.tag) {
- case ClassComponent: {
- if (finishedWork.effectTag & Snapshot) {
- if (current !== null) {
- const prevProps = current.memoizedProps;
- const prevState = current.memoizedState;
- startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
- const instance = finishedWork.stateNode;
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- const snapshot = instance.getSnapshotBeforeUpdate(
- prevProps,
- prevState,
- );
- if (__DEV__) {
- const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<
- mixed,
- >);
- if (
- snapshot === undefined &&
- !didWarnSet.has(finishedWork.type)
- ) {
- didWarnSet.add(finishedWork.type);
- warning(
- false,
- '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
- 'must be returned. You have returned undefined.',
- getComponentName(finishedWork),
- );
- }
+function commitBeforeMutationLifeCycles(
+ current: Fiber | null,
+ finishedWork: Fiber,
+): void {
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ if (finishedWork.effectTag & Snapshot) {
+ if (current !== null) {
+ const prevProps = current.memoizedProps;
+ const prevState = current.memoizedState;
+ startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
+ const instance = finishedWork.stateNode;
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ const snapshot = instance.getSnapshotBeforeUpdate(
+ prevProps,
+ prevState,
+ );
+ if (__DEV__) {
+ const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<
+ mixed,
+ >);
+ if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
+ didWarnSet.add(finishedWork.type);
+ warning(
+ false,
+ '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
+ 'must be returned. You have returned undefined.',
+ getComponentName(finishedWork),
+ );
}
- instance.__reactInternalSnapshotBeforeUpdate = snapshot;
- stopPhaseTimer();
}
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
+ stopPhaseTimer();
}
- return;
- }
- case HostRoot:
- case HostComponent:
- case HostText:
- case HostPortal:
- // Nothing to do for these component types
- 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.',
- );
}
+ return;
+ }
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ // Nothing to do for these component types
+ 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 commitLifeCycles(
- finishedRoot: FiberRoot,
- current: Fiber | null,
- finishedWork: Fiber,
- currentTime: ExpirationTime,
- committedExpirationTime: ExpirationTime,
- ): void {
- switch (finishedWork.tag) {
- case ClassComponent: {
- const instance = finishedWork.stateNode;
- if (finishedWork.effectTag & Update) {
- if (current === null) {
- startPhaseTimer(finishedWork, 'componentDidMount');
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidMount();
- stopPhaseTimer();
- } else {
- const prevProps = current.memoizedProps;
- const prevState = current.memoizedState;
- startPhaseTimer(finishedWork, 'componentDidUpdate');
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidUpdate(
- prevProps,
- prevState,
- instance.__reactInternalSnapshotBeforeUpdate,
- );
- stopPhaseTimer();
- }
- }
- const updateQueue = finishedWork.updateQueue;
- if (updateQueue !== null) {
+function commitLifeCycles(
+ finishedRoot: FiberRoot,
+ current: Fiber | null,
+ finishedWork: Fiber,
+ currentTime: ExpirationTime,
+ committedExpirationTime: ExpirationTime,
+): void {
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ const instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current === null) {
+ startPhaseTimer(finishedWork, 'componentDidMount');
instance.props = finishedWork.memoizedProps;
instance.state = finishedWork.memoizedState;
- commitUpdateQueue(
- finishedWork,
- updateQueue,
- instance,
- committedExpirationTime,
+ instance.componentDidMount();
+ stopPhaseTimer();
+ } else {
+ const prevProps = current.memoizedProps;
+ const prevState = current.memoizedState;
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ instance.componentDidUpdate(
+ prevProps,
+ prevState,
+ instance.__reactInternalSnapshotBeforeUpdate,
);
+ stopPhaseTimer();
}
- return;
}
- case HostRoot: {
- const updateQueue = finishedWork.updateQueue;
- if (updateQueue !== null) {
- let instance = null;
- if (finishedWork.child !== null) {
- switch (finishedWork.child.tag) {
- case HostComponent:
- instance = getPublicInstance(finishedWork.child.stateNode);
- break;
- case ClassComponent:
- instance = finishedWork.child.stateNode;
- break;
- }
- }
- commitUpdateQueue(
- finishedWork,
- updateQueue,
- instance,
- committedExpirationTime,
- );
- }
- return;
+ const updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ commitUpdateQueue(
+ finishedWork,
+ updateQueue,
+ instance,
+ committedExpirationTime,
+ );
}
- 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 HostRoot: {
+ const updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ let instance = null;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostComponent:
+ instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case ClassComponent:
+ instance = finishedWork.child.stateNode;
+ break;
+ }
}
-
- return;
- }
- case HostText: {
- // We have no life-cycles associated with text.
- return;
- }
- case HostPortal: {
- // We have no life-cycles associated with portals.
- return;
- }
- case Profiler: {
- // We have no life-cycles associated with Profiler.
- return;
- }
- case TimeoutComponent: {
- // We have no life-cycles associated with Timeouts.
- 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.',
+ commitUpdateQueue(
+ finishedWork,
+ updateQueue,
+ instance,
+ committedExpirationTime,
);
}
+ return;
}
- }
+ case HostComponent: {
+ const instance: Instance = finishedWork.stateNode;
- function commitAttachRef(finishedWork: Fiber) {
- const ref = finishedWork.ref;
- if (ref !== null) {
- const instance = finishedWork.stateNode;
- let instanceToUse;
- switch (finishedWork.tag) {
- case HostComponent:
- instanceToUse = getPublicInstance(instance);
- break;
- default:
- instanceToUse = instance;
+ // 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);
}
- if (typeof ref === 'function') {
- ref(instanceToUse);
- } else {
- if (__DEV__) {
- if (!ref.hasOwnProperty('current')) {
- warning(
- false,
- 'Unexpected ref object provided for %s. ' +
- 'Use either a ref-setter function or React.createRef().%s',
- getComponentName(finishedWork),
- getStackAddendumByWorkInProgressFiber(finishedWork),
- );
- }
- }
- ref.current = instanceToUse;
- }
+ return;
+ }
+ case HostText: {
+ // We have no life-cycles associated with text.
+ return;
+ }
+ case HostPortal: {
+ // We have no life-cycles associated with portals.
+ return;
+ }
+ case Profiler: {
+ // We have no life-cycles associated with Profiler.
+ return;
+ }
+ case TimeoutComponent: {
+ // We have no life-cycles associated with Timeouts.
+ 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 commitDetachRef(current: Fiber) {
- const currentRef = current.ref;
- if (currentRef !== null) {
- if (typeof currentRef === 'function') {
- currentRef(null);
- } else {
- currentRef.current = null;
+function commitAttachRef(finishedWork: Fiber) {
+ const ref = finishedWork.ref;
+ if (ref !== null) {
+ const instance = finishedWork.stateNode;
+ let instanceToUse;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ instanceToUse = getPublicInstance(instance);
+ break;
+ default:
+ instanceToUse = instance;
+ }
+ if (typeof ref === 'function') {
+ ref(instanceToUse);
+ } else {
+ if (__DEV__) {
+ if (!ref.hasOwnProperty('current')) {
+ warning(
+ false,
+ 'Unexpected ref object provided for %s. ' +
+ 'Use either a ref-setter function or React.createRef().%s',
+ getComponentName(finishedWork),
+ getStackAddendumByWorkInProgressFiber(finishedWork),
+ );
+ }
}
+
+ ref.current = instanceToUse;
}
}
+}
- // 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);
+function commitDetachRef(current: Fiber) {
+ const currentRef = current.ref;
+ if (currentRef !== null) {
+ if (typeof currentRef === 'function') {
+ currentRef(null);
+ } else {
+ currentRef.current = null;
}
+ }
+}
- 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 HostPortal: {
- // TODO: this is recursive.
- // We are also not using this parent because
- // the portal will get pushed immediately.
- if (enableMutatingReconciler && mutation) {
- unmountHostComponents(current);
- } else if (enablePersistentReconciler && persistence) {
- emptyPortalContainer(current);
- }
- return;
+// 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 HostPortal: {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ if (supportsMutation) {
+ unmountHostComponents(current);
+ } else if (supportsPersistence) {
+ emptyPortalContainer(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 &&
- // If we use mutation we drill down into portals using commitUnmount above.
- // If we don't use mutation we drill down into portals here instead.
- (!mutation || node.tag !== HostPortal)
- ) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- if (node === root) {
+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 &&
+ // If we use mutation we drill down into portals using commitUnmount above.
+ // If we don't use mutation we drill down into portals here instead.
+ (!supportsMutation || 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;
}
- while (node.sibling === null) {
- if (node.return === null || node.return === root) {
- return;
- }
- node = node.return;
- }
- node.sibling.return = node.return;
- node = node.sibling;
+ 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;
+ }
+}
+
+function emptyPortalContainer(current: Fiber) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ const portal: {containerInfo: Container, pendingChildren: ChildSet} =
+ current.stateNode;
+ const {containerInfo} = portal;
+ const emptyChildSet = createContainerChildSet(containerInfo);
+ replaceContainerChildren(containerInfo, emptyChildSet);
+}
+
+function commitContainer(finishedWork: Fiber) {
+ if (!supportsPersistence) {
+ return;
}
- 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;
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ return;
+ }
+ case HostComponent: {
+ return;
+ }
+ case HostText: {
+ return;
+ }
+ case HostRoot:
+ case HostPortal: {
+ const portalOrRoot: {
+ containerInfo: Container,
+ pendingChildren: ChildSet,
+ } =
+ finishedWork.stateNode;
+ const {containerInfo, pendingChildren} = portalOrRoot;
+ replaceContainerChildren(containerInfo, pendingChildren);
+ 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.',
+ );
}
}
+}
- let emptyPortalContainer;
-
- if (!mutation) {
- let commitContainer;
- if (persistence) {
- const {replaceContainerChildren, createContainerChildSet} = persistence;
- emptyPortalContainer = function(current: Fiber) {
- const portal: {containerInfo: C, pendingChildren: CC} =
- current.stateNode;
- const {containerInfo} = portal;
- const emptyChildSet = createContainerChildSet(containerInfo);
- replaceContainerChildren(containerInfo, emptyChildSet);
- };
- commitContainer = function(finishedWork: Fiber) {
- switch (finishedWork.tag) {
- case ClassComponent: {
- return;
- }
- case HostComponent: {
- return;
- }
- case HostText: {
- return;
- }
- case HostRoot:
- case HostPortal: {
- const portalOrRoot: {containerInfo: C, pendingChildren: CC} =
- finishedWork.stateNode;
- const {containerInfo, pendingChildren} = portalOrRoot;
- replaceContainerChildren(containerInfo, pendingChildren);
- 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.',
- );
- }
- }
- };
- } else {
- commitContainer = function(finishedWork: Fiber) {
- // Noop
- };
- }
- if (enablePersistentReconciler || enableNoopReconciler) {
- 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) {
- commitContainer(finishedWork);
- },
- commitLifeCycles,
- commitBeforeMutationLifeCycles,
- commitAttachRef,
- commitDetachRef,
- };
- } else if (persistence) {
- invariant(false, 'Persistent reconciler is disabled.');
- } else {
- invariant(false, 'Noop reconciler is disabled.');
+function getHostParentFiber(fiber: Fiber): Fiber {
+ let parent = fiber.return;
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
}
+ parent = parent.return;
}
- const {
- commitMount,
- commitUpdate,
- resetTextContent,
- commitTextUpdate,
- appendChild,
- appendChildToContainer,
- insertBefore,
- insertInContainerBefore,
- removeChild,
- removeChildFromContainer,
- } = mutation;
-
- function getHostParentFiber(fiber: Fiber): Fiber {
- let parent = fiber.return;
- while (parent !== null) {
- if (isHostParent(parent)) {
- return parent;
+ invariant(
+ false,
+ 'Expected to find a host parent. This error is likely caused by a bug ' +
+ 'in React. Please file an issue.',
+ );
+}
+
+function isHostParent(fiber: Fiber): boolean {
+ return (
+ fiber.tag === HostComponent ||
+ fiber.tag === HostRoot ||
+ fiber.tag === HostPortal
+ );
+}
+
+function getHostSibling(fiber: Fiber): ?Instance {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ // TODO: Find a more efficient way to do this.
+ let node: Fiber = fiber;
+ siblings: while (true) {
+ // If we didn't find anything, let's try the next sibling.
+ while (node.sibling === null) {
+ if (node.return === null || isHostParent(node.return)) {
+ // If we pop out of the root or hit the parent the fiber we are the
+ // last sibling.
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ while (node.tag !== HostComponent && node.tag !== HostText) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.effectTag & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ }
+ // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child.return = node;
+ node = node.child;
}
- parent = parent.return;
}
- invariant(
- false,
- 'Expected to find a host parent. This error is likely caused by a bug ' +
- 'in React. Please file an issue.',
- );
+ // Check if this host node is stable or about to be placed.
+ if (!(node.effectTag & Placement)) {
+ // Found it!
+ return node.stateNode;
+ }
}
+}
- function isHostParent(fiber: Fiber): boolean {
- return (
- fiber.tag === HostComponent ||
- fiber.tag === HostRoot ||
- fiber.tag === HostPortal
- );
+function commitPlacement(finishedWork: Fiber): void {
+ if (!supportsMutation) {
+ return;
}
- function getHostSibling(fiber: Fiber): ?I {
- // We're going to search forward into the tree until we find a sibling host
- // node. Unfortunately, if multiple insertions are done in a row we have to
- // search past them. This leads to exponential search for the next sibling.
- // TODO: Find a more efficient way to do this.
- let node: Fiber = fiber;
- siblings: while (true) {
- // If we didn't find anything, let's try the next sibling.
- while (node.sibling === null) {
- if (node.return === null || isHostParent(node.return)) {
- // If we pop out of the root or hit the parent the fiber we are the
- // last sibling.
- return null;
- }
- node = node.return;
- }
- node.sibling.return = node.return;
- node = node.sibling;
- while (node.tag !== HostComponent && node.tag !== HostText) {
- // If it is not host node and, we might have a host node inside it.
- // Try to search down until we find one.
- if (node.effectTag & Placement) {
- // If we don't have a child, try the siblings instead.
- continue siblings;
+ // Recursively insert all host nodes into the parent.
+ const parentFiber = getHostParentFiber(finishedWork);
+ let parent;
+ let isContainer;
+ switch (parentFiber.tag) {
+ case HostComponent:
+ parent = parentFiber.stateNode;
+ isContainer = false;
+ break;
+ case HostRoot:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ case HostPortal:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ default:
+ invariant(
+ false,
+ 'Invalid host parent fiber. This error is likely caused by a bug ' +
+ 'in React. Please file an issue.',
+ );
+ }
+ if (parentFiber.effectTag & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ resetTextContent(parent);
+ // Clear ContentReset from the effect tag
+ parentFiber.effectTag &= ~ContentReset;
+ }
+
+ const before = getHostSibling(finishedWork);
+ // We only have the top Fiber that was inserted but we need recurse down its
+ // children to find all the terminal nodes.
+ let node: Fiber = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ if (before) {
+ if (isContainer) {
+ insertInContainerBefore(parent, node.stateNode, before);
+ } else {
+ insertBefore(parent, node.stateNode, before);
}
- // If we don't have a child, try the siblings instead.
- // We also skip portals because they are not part of this host tree.
- if (node.child === null || node.tag === HostPortal) {
- continue siblings;
+ } else {
+ if (isContainer) {
+ appendChildToContainer(parent, node.stateNode);
} else {
- node.child.return = node;
- node = node.child;
+ appendChild(parent, node.stateNode);
}
}
- // Check if this host node is stable or about to be placed.
- if (!(node.effectTag & Placement)) {
- // Found it!
- return node.stateNode;
+ } else if (node.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
}
+ node = node.return;
}
+ node.sibling.return = node.return;
+ node = node.sibling;
}
+}
- function commitPlacement(finishedWork: Fiber): void {
- // Recursively insert all host nodes into the parent.
- const parentFiber = getHostParentFiber(finishedWork);
- let parent;
- let isContainer;
- switch (parentFiber.tag) {
- case HostComponent:
- parent = parentFiber.stateNode;
- isContainer = false;
- break;
- case HostRoot:
- parent = parentFiber.stateNode.containerInfo;
- isContainer = true;
- break;
- case HostPortal:
- parent = parentFiber.stateNode.containerInfo;
- isContainer = true;
- break;
- default:
+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.
+ let node: Fiber = current;
+
+ // Each iteration, currentParent is populated with node's host parent if not
+ // currentParentIsValid.
+ let currentParentIsValid = false;
+ let currentParent;
+ let currentParentIsContainer;
+
+ while (true) {
+ if (!currentParentIsValid) {
+ let parent = node.return;
+ findParent: while (true) {
invariant(
- false,
- 'Invalid host parent fiber. This error is likely caused by a bug ' +
- 'in React. Please file an issue.',
+ parent !== null,
+ 'Expected to find a host parent. This error is likely caused by ' +
+ 'a bug in React. Please file an issue.',
);
- }
- if (parentFiber.effectTag & ContentReset) {
- // Reset the text content of the parent before doing any insertions
- resetTextContent(parent);
- // Clear ContentReset from the effect tag
- parentFiber.effectTag &= ~ContentReset;
- }
-
- const before = getHostSibling(finishedWork);
- // We only have the top Fiber that was inserted but we need recurse down its
- // children to find all the terminal nodes.
- let node: Fiber = finishedWork;
- while (true) {
- if (node.tag === HostComponent || node.tag === HostText) {
- if (before) {
- if (isContainer) {
- insertInContainerBefore(parent, node.stateNode, before);
- } else {
- insertBefore(parent, node.stateNode, before);
- }
- } else {
- if (isContainer) {
- appendChildToContainer(parent, node.stateNode);
- } else {
- appendChild(parent, node.stateNode);
- }
+ switch (parent.tag) {
+ case HostComponent:
+ currentParent = parent.stateNode;
+ currentParentIsContainer = false;
+ break findParent;
+ case HostRoot:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ case HostPortal:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
}
- } else if (node.tag === HostPortal) {
- // If the insertion itself is a portal, then we don't want to traverse
- // down its children. Instead, we'll get insertions from each child in
- // the portal directly.
- } else if (node.child !== null) {
+ parent = parent.return;
+ }
+ currentParentIsValid = true;
+ }
+
+ if (node.tag === HostComponent || node.tag === HostText) {
+ commitNestedUnmounts(node);
+ // After all the children have unmounted, it is now safe to remove the
+ // node from the tree.
+ if (currentParentIsContainer) {
+ removeChildFromContainer((currentParent: any), node.stateNode);
+ } else {
+ removeChild((currentParent: any), node.stateNode);
+ }
+ // Don't visit children because we already visited them.
+ } else if (node.tag === HostPortal) {
+ // When we go into a portal, it becomes the parent to remove from.
+ // We will reassign it back when we pop the portal on the way up.
+ currentParent = node.stateNode.containerInfo;
+ // Visit children because portals might contain host components.
+ if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
- if (node === finishedWork) {
- return;
- }
- while (node.sibling === null) {
- if (node.return === null || node.return === finishedWork) {
- return;
- }
- node = node.return;
+ } else {
+ commitUnmount(node);
+ // Visit children because we may find more host components below.
+ if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
}
- 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.
- let node: Fiber = current;
-
- // Each iteration, currentParent is populated with node's host parent if not
- // currentParentIsValid.
- let currentParentIsValid = false;
- let currentParent;
- let currentParentIsContainer;
-
- while (true) {
- if (!currentParentIsValid) {
- let parent = node.return;
- findParent: while (true) {
- invariant(
- parent !== null,
- 'Expected to find a host parent. This error is likely caused by ' +
- 'a bug in React. Please file an issue.',
- );
- switch (parent.tag) {
- case HostComponent:
- currentParent = parent.stateNode;
- currentParentIsContainer = false;
- break findParent;
- case HostRoot:
- currentParent = parent.stateNode.containerInfo;
- currentParentIsContainer = true;
- break findParent;
- case HostPortal:
- currentParent = parent.stateNode.containerInfo;
- currentParentIsContainer = true;
- break findParent;
- }
- parent = parent.return;
- }
- currentParentIsValid = true;
- }
-
- if (node.tag === HostComponent || node.tag === HostText) {
- commitNestedUnmounts(node);
- // After all the children have unmounted, it is now safe to remove the
- // node from the tree.
- if (currentParentIsContainer) {
- removeChildFromContainer((currentParent: any), node.stateNode);
- } else {
- removeChild((currentParent: any), node.stateNode);
- }
- // Don't visit children because we already visited them.
- } else if (node.tag === HostPortal) {
- // When we go into a portal, it becomes the parent to remove from.
- // We will reassign it back when we pop the portal on the way up.
- currentParent = node.stateNode.containerInfo;
- // Visit children because portals might contain host components.
- if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- } else {
- commitUnmount(node);
- // Visit children because we may find more host components below.
- if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- }
- if (node === current) {
+ if (node === current) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === current) {
return;
}
- while (node.sibling === null) {
- if (node.return === null || node.return === current) {
- return;
- }
- node = node.return;
- if (node.tag === HostPortal) {
- // When we go out of the portal, we need to restore the parent.
- // Since we don't keep a stack of them, we will search for it.
- currentParentIsValid = false;
- }
+ node = node.return;
+ if (node.tag === HostPortal) {
+ // When we go out of the portal, we need to restore the parent.
+ // Since we don't keep a stack of them, we will search for it.
+ currentParentIsValid = false;
}
- node.sibling.return = node.return;
- node = node.sibling;
}
+ node.sibling.return = node.return;
+ node = node.sibling;
}
+}
- function commitDeletion(current: Fiber): void {
+function commitDeletion(current: Fiber): void {
+ if (supportsMutation) {
// Recursively delete all host nodes from the parent.
// Detach refs and call componentWillUnmount() on the whole subtree.
unmountHostComponents(current);
- detachFiber(current);
+ } else {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current);
}
+ detachFiber(current);
+}
- function commitWork(current: Fiber | null, finishedWork: Fiber): void {
- switch (finishedWork.tag) {
- case ClassComponent: {
- return;
- }
- case HostComponent: {
- const instance: I = finishedWork.stateNode;
- if (instance != null) {
- // Commit the work prepared earlier.
- const newProps = finishedWork.memoizedProps;
- // For hydration we reuse the update path but we treat the oldProps
- // as the newProps. The updatePayload will contain the real change in
- // this case.
- const oldProps = current !== null ? current.memoizedProps : newProps;
- const type = finishedWork.type;
- // TODO: Type the updateQueue to be specific to host components.
- const updatePayload: null | PL = (finishedWork.updateQueue: any);
- finishedWork.updateQueue = null;
- if (updatePayload !== null) {
- commitUpdate(
- instance,
- updatePayload,
- type,
- oldProps,
- newProps,
- finishedWork,
- );
- }
- }
- return;
- }
- case HostText: {
- invariant(
- finishedWork.stateNode !== null,
- 'This should have a text node initialized. This error is likely ' +
- 'caused by a bug in React. Please file an issue.',
- );
- const textInstance: TI = finishedWork.stateNode;
- const newText: string = finishedWork.memoizedProps;
+function commitWork(current: Fiber | null, finishedWork: Fiber): void {
+ if (!supportsMutation) {
+ commitContainer(finishedWork);
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ return;
+ }
+ case HostComponent: {
+ const instance: Instance = finishedWork.stateNode;
+ if (instance != null) {
+ // Commit the work prepared earlier.
+ const newProps = finishedWork.memoizedProps;
// For hydration we reuse the update path but we treat the oldProps
// as the newProps. The updatePayload will contain the real change in
// this case.
- const oldText: string =
- current !== null ? current.memoizedProps : newText;
- commitTextUpdate(textInstance, oldText, newText);
- return;
- }
- case HostRoot: {
- return;
- }
- case Profiler: {
- if (enableProfilerTimer) {
- const onRender = finishedWork.memoizedProps.onRender;
- onRender(
- finishedWork.memoizedProps.id,
- current === null ? 'mount' : 'update',
- finishedWork.stateNode.duration,
- finishedWork.treeBaseTime,
+ const oldProps = current !== null ? current.memoizedProps : newProps;
+ const type = finishedWork.type;
+ // TODO: Type the updateQueue to be specific to host components.
+ const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
+ finishedWork.updateQueue = null;
+ if (updatePayload !== null) {
+ commitUpdate(
+ instance,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ finishedWork,
);
-
- // Reset actualTime after successful commit.
- // By default, we append to this time to account for errors and pauses.
- finishedWork.stateNode.duration = 0;
}
- return;
- }
- case TimeoutComponent: {
- 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.',
+ return;
+ }
+ case HostText: {
+ invariant(
+ finishedWork.stateNode !== null,
+ 'This should have a text node initialized. This error is likely ' +
+ 'caused by a bug in React. Please file an issue.',
+ );
+ const textInstance: TextInstance = finishedWork.stateNode;
+ const newText: string = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ const oldText: string =
+ current !== null ? current.memoizedProps : newText;
+ commitTextUpdate(textInstance, oldText, newText);
+ return;
+ }
+ case HostRoot: {
+ return;
+ }
+ case Profiler: {
+ if (enableProfilerTimer) {
+ const onRender = finishedWork.memoizedProps.onRender;
+ onRender(
+ finishedWork.memoizedProps.id,
+ current === null ? 'mount' : 'update',
+ finishedWork.stateNode.duration,
+ finishedWork.treeBaseTime,
);
+
+ // Reset actualTime after successful commit.
+ // By default, we append to this time to account for errors and pauses.
+ finishedWork.stateNode.duration = 0;
}
+ return;
+ }
+ case TimeoutComponent: {
+ 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 commitResetTextContent(current: Fiber) {
- resetTextContent(current.stateNode);
- }
-
- if (enableMutatingReconciler) {
- return {
- commitBeforeMutationLifeCycles,
- commitResetTextContent,
- commitPlacement,
- commitDeletion,
- commitWork,
- commitLifeCycles,
- commitAttachRef,
- commitDetachRef,
- };
- } else {
- invariant(false, 'Mutating reconciler is disabled.');
+function commitResetTextContent(current: Fiber) {
+ if (!supportsMutation) {
+ return;
}
+ resetTextContent(current.stateNode);
}
+
+export {
+ commitBeforeMutationLifeCycles,
+ commitResetTextContent,
+ commitPlacement,
+ commitDeletion,
+ commitWork,
+ commitLifeCycles,
+ commitAttachRef,
+ commitDetachRef,
+};
diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js
index 9d2cec1f3586f..8ed630b82db25 100644
--- a/packages/react-reconciler/src/ReactFiberCompleteWork.js
+++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js
@@ -7,22 +7,20 @@
* @flow
*/
-import type {HostConfig} from 'react-reconciler';
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
-import type {HostContext} from './ReactFiberHostContext';
-import type {LegacyContext} from './ReactFiberContext';
-import type {NewContext} from './ReactFiberNewContext';
-import type {HydrationContext} from './ReactFiberHydrationContext';
import type {FiberRoot} from './ReactFiberRoot';
-import type {ProfilerTimer} from './ReactProfilerTimer';
+import type {
+ Instance,
+ Type,
+ Props,
+ UpdatePayload,
+ Container,
+ ChildSet,
+ HostContext,
+} from './ReactFiberHostConfig';
-import {
- enableMutatingReconciler,
- enablePersistentReconciler,
- enableNoopReconciler,
- enableProfilerTimer,
-} from 'shared/ReactFeatureFlags';
+import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
import {
IndeterminateComponent,
FunctionalComponent,
@@ -42,63 +40,129 @@ import {
import {Placement, Ref, Update} from 'shared/ReactTypeOfSideEffect';
import invariant from 'fbjs/lib/invariant';
-export default function(
- config: HostConfig,
- hostContext: HostContext,
- legacyContext: LegacyContext,
- newContext: NewContext,
- hydrationContext: HydrationContext,
- profilerTimer: ProfilerTimer,
-) {
- const {
- createInstance,
- createTextInstance,
- appendInitialChild,
- finalizeInitialChildren,
- prepareUpdate,
- mutation,
- persistence,
- } = config;
-
- const {
- getRootHostContainer,
- popHostContext,
- getHostContext,
- popHostContainer,
- } = hostContext;
-
- const {recordElapsedActualRenderTime} = profilerTimer;
-
- const {
- popContextProvider: popLegacyContextProvider,
- popTopLevelContextObject: popTopLevelLegacyContextObject,
- } = legacyContext;
+import {
+ createInstance,
+ createTextInstance,
+ appendInitialChild,
+ finalizeInitialChildren,
+ prepareUpdate,
+ supportsMutation,
+ supportsPersistence,
+ cloneInstance,
+ createContainerChildSet,
+ appendChildToContainerChildSet,
+ finalizeContainerChildren,
+} from './ReactFiberHostConfig';
+import {
+ getRootHostContainer,
+ popHostContext,
+ getHostContext,
+ popHostContainer,
+} from './ReactFiberHostContext';
+import {recordElapsedActualRenderTime} from './ReactProfilerTimer';
+import {
+ popContextProvider as popLegacyContextProvider,
+ popTopLevelContextObject as popTopLevelLegacyContextObject,
+} from './ReactFiberContext';
+import {popProvider} from './ReactFiberNewContext';
+import {
+ prepareToHydrateHostInstance,
+ prepareToHydrateHostTextInstance,
+ popHydrationState,
+} from './ReactFiberHydrationContext';
- const {popProvider} = newContext;
+function markUpdate(workInProgress: Fiber) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // a PlacementAndUpdate.
+ workInProgress.effectTag |= Update;
+}
- const {
- prepareToHydrateHostInstance,
- prepareToHydrateHostTextInstance,
- popHydrationState,
- } = hydrationContext;
+function markRef(workInProgress: Fiber) {
+ workInProgress.effectTag |= Ref;
+}
- function markUpdate(workInProgress: Fiber) {
- // Tag the fiber with an update effect. This turns a Placement into
- // a PlacementAndUpdate.
- workInProgress.effectTag |= Update;
+function appendAllChildren(parent: Instance, workInProgress: Fiber) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ let node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
}
+}
- function markRef(workInProgress: Fiber) {
- workInProgress.effectTag |= Ref;
- }
+let updateHostContainer;
+let updateHostComponent;
+let updateHostText;
+if (supportsMutation) {
+ // Mutation mode
+
+ updateHostContainer = function(workInProgress: Fiber) {
+ // Noop
+ };
+ updateHostComponent = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ updatePayload: null | UpdatePayload,
+ type: Type,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ currentHostContext: HostContext,
+ ) {
+ // TODO: Type this specific to this type of component.
+ workInProgress.updateQueue = (updatePayload: any);
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update. All the work is done in commitWork.
+ if (updatePayload) {
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostText = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ oldText: string,
+ newText: string,
+ ) {
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
+ }
+ };
+} else if (supportsPersistence) {
+ // Persistent host tree mode
- function appendAllChildren(parent: I, workInProgress: Fiber) {
+ // An unfortunate fork of appendAllChildren because we have two different parent types.
+ const appendAllChildrenToContainer = function(
+ containerChildSet: ChildSet,
+ workInProgress: Fiber,
+ ) {
// We only have the top Fiber that was created but we need recurse down its
// children to find all the terminal nodes.
let node = workInProgress.child;
while (node !== null) {
if (node.tag === HostComponent || node.tag === HostText) {
- appendInitialChild(parent, node.stateNode);
+ appendChildToContainerChildSet(containerChildSet, node.stateNode);
} else if (node.tag === HostPortal) {
// If we have a portal child, then we don't want to traverse
// down its children. Instead, we'll get insertions from each child in
@@ -120,433 +184,341 @@ export default function(
node.sibling.return = node.return;
node = node.sibling;
}
- }
-
- let updateHostContainer;
- let updateHostComponent;
- let updateHostText;
- if (mutation) {
- if (enableMutatingReconciler) {
- // Mutation mode
- updateHostContainer = function(workInProgress: Fiber) {
- // Noop
- };
- updateHostComponent = function(
- current: Fiber,
- workInProgress: Fiber,
- updatePayload: null | PL,
- type: T,
- oldProps: P,
- newProps: P,
- rootContainerInstance: C,
- currentHostContext: CX,
- ) {
- // TODO: Type this specific to this type of component.
- workInProgress.updateQueue = (updatePayload: any);
- // If the update payload indicates that there is a change or if there
- // is a new ref we mark this as an update. All the work is done in commitWork.
- if (updatePayload) {
- markUpdate(workInProgress);
- }
- };
- updateHostText = function(
- current: Fiber,
- workInProgress: Fiber,
- oldText: string,
- newText: string,
- ) {
- // If the text differs, mark it as an update. All the work in done in commitWork.
- if (oldText !== newText) {
- markUpdate(workInProgress);
- }
- };
+ };
+ updateHostContainer = function(workInProgress: Fiber) {
+ const portalOrRoot: {
+ containerInfo: Container,
+ pendingChildren: ChildSet,
+ } =
+ workInProgress.stateNode;
+ const childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged) {
+ // No changes, just reuse the existing instance.
} else {
- invariant(false, 'Mutating reconciler is disabled.');
+ const container = portalOrRoot.containerInfo;
+ let newChildSet = createContainerChildSet(container);
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildrenToContainer(newChildSet, workInProgress);
+ portalOrRoot.pendingChildren = newChildSet;
+ // Schedule an update on the container to swap out the container.
+ markUpdate(workInProgress);
+ finalizeContainerChildren(container, newChildSet);
}
- } else if (persistence) {
- if (enablePersistentReconciler) {
- // Persistent host tree mode
- const {
- cloneInstance,
- createContainerChildSet,
- appendChildToContainerChildSet,
- finalizeContainerChildren,
- } = persistence;
-
- // An unfortunate fork of appendAllChildren because we have two different parent types.
- const appendAllChildrenToContainer = function(
- containerChildSet: CC,
- workInProgress: Fiber,
+ };
+ updateHostComponent = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ updatePayload: null | UpdatePayload,
+ type: Type,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ currentHostContext: HostContext,
+ ) {
+ // If there are no effects associated with this node, then none of our children had any updates.
+ // This guarantees that we can reuse all of them.
+ const childrenUnchanged = workInProgress.firstEffect === null;
+ const currentInstance = current.stateNode;
+ if (childrenUnchanged && updatePayload === null) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ } else {
+ let recyclableInstance = workInProgress.stateNode;
+ let newInstance = cloneInstance(
+ currentInstance,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ workInProgress,
+ childrenUnchanged,
+ recyclableInstance,
+ );
+ if (
+ finalizeInitialChildren(
+ newInstance,
+ type,
+ newProps,
+ rootContainerInstance,
+ currentHostContext,
+ )
) {
- // We only have the top Fiber that was created but we need recurse down its
- // children to find all the terminal nodes.
- let node = workInProgress.child;
- while (node !== null) {
- if (node.tag === HostComponent || node.tag === HostText) {
- appendChildToContainerChildSet(containerChildSet, node.stateNode);
- } else if (node.tag === HostPortal) {
- // If we have a portal child, then we don't want to traverse
- // down its children. Instead, we'll get insertions from each child in
- // the portal directly.
- } else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- if (node === workInProgress) {
- return;
- }
- while (node.sibling === null) {
- if (node.return === null || node.return === workInProgress) {
- return;
- }
- node = node.return;
- }
- node.sibling.return = node.return;
- node = node.sibling;
- }
- };
- updateHostContainer = function(workInProgress: Fiber) {
- const portalOrRoot: {containerInfo: C, pendingChildren: CC} =
- workInProgress.stateNode;
- const childrenUnchanged = workInProgress.firstEffect === null;
- if (childrenUnchanged) {
- // No changes, just reuse the existing instance.
- } else {
- const container = portalOrRoot.containerInfo;
- let newChildSet = createContainerChildSet(container);
- // If children might have changed, we have to add them all to the set.
- appendAllChildrenToContainer(newChildSet, workInProgress);
- portalOrRoot.pendingChildren = newChildSet;
- // Schedule an update on the container to swap out the container.
- markUpdate(workInProgress);
- finalizeContainerChildren(container, newChildSet);
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = newInstance;
+ if (childrenUnchanged) {
+ // If there are no other effects in this tree, we need to flag this node as having one.
+ // Even though we're not going to use it for anything.
+ // Otherwise parents won't know that there are new children to propagate upwards.
+ markUpdate(workInProgress);
+ } else {
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildren(newInstance, workInProgress);
+ }
+ }
+ };
+ updateHostText = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ oldText: string,
+ newText: string,
+ ) {
+ if (oldText !== newText) {
+ // If the text content differs, we'll create a new text instance for it.
+ const rootContainerInstance = getRootHostContainer();
+ const currentHostContext = getHostContext();
+ workInProgress.stateNode = createTextInstance(
+ newText,
+ rootContainerInstance,
+ currentHostContext,
+ workInProgress,
+ );
+ // We'll have to mark it as having an effect, even though we won't use the effect for anything.
+ // This lets the parents know that at least one of their children has changed.
+ markUpdate(workInProgress);
+ }
+ };
+} else {
+ // No host operations
+ updateHostContainer = function(workInProgress: Fiber) {
+ // Noop
+ };
+ updateHostComponent = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ updatePayload: null | UpdatePayload,
+ type: Type,
+ oldProps: Props,
+ newProps: Props,
+ rootContainerInstance: Container,
+ currentHostContext: HostContext,
+ ) {
+ // Noop
+ };
+ updateHostText = function(
+ current: Fiber,
+ workInProgress: Fiber,
+ oldText: string,
+ newText: string,
+ ) {
+ // Noop
+ };
+}
+
+function completeWork(
+ current: Fiber | null,
+ workInProgress: Fiber,
+ renderExpirationTime: ExpirationTime,
+): Fiber | null {
+ const newProps = workInProgress.pendingProps;
+ switch (workInProgress.tag) {
+ case FunctionalComponent:
+ return null;
+ case ClassComponent: {
+ // We are leaving this subtree, so pop context if any.
+ popLegacyContextProvider(workInProgress);
+ return null;
+ }
+ case HostRoot: {
+ popHostContainer(workInProgress);
+ popTopLevelLegacyContextObject(workInProgress);
+ const fiberRoot = (workInProgress.stateNode: FiberRoot);
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ popHydrationState(workInProgress);
+ // This resets the hacky state to fix isMounted before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag &= ~Placement;
+ }
+ updateHostContainer(workInProgress);
+ return null;
+ }
+ case HostComponent: {
+ popHostContext(workInProgress);
+ const rootContainerInstance = getRootHostContainer();
+ const type = workInProgress.type;
+ if (current !== null && workInProgress.stateNode != null) {
+ // If we have an alternate, that means this is an update and we need to
+ // schedule a side-effect to do the updates.
+ const oldProps = current.memoizedProps;
+ // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
+ const instance: Instance = workInProgress.stateNode;
+ const currentHostContext = getHostContext();
+ // TODO: Experiencing an error where oldProps is null. Suggests a host
+ // component is hitting the resume path. Figure out why. Possibly
+ // related to `hidden`.
+ const updatePayload = prepareUpdate(
+ instance,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext,
+ );
+
+ updateHostComponent(
+ current,
+ workInProgress,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext,
+ );
+
+ if (current.ref !== workInProgress.ref) {
+ markRef(workInProgress);
}
- };
- updateHostComponent = function(
- current: Fiber,
- workInProgress: Fiber,
- updatePayload: null | PL,
- type: T,
- oldProps: P,
- newProps: P,
- rootContainerInstance: C,
- currentHostContext: CX,
- ) {
- // If there are no effects associated with this node, then none of our children had any updates.
- // This guarantees that we can reuse all of them.
- const childrenUnchanged = workInProgress.firstEffect === null;
- const currentInstance = current.stateNode;
- if (childrenUnchanged && updatePayload === null) {
- // No changes, just reuse the existing instance.
- // Note that this might release a previous clone.
- workInProgress.stateNode = currentInstance;
- } else {
- let recyclableInstance = workInProgress.stateNode;
- let newInstance = cloneInstance(
- currentInstance,
- updatePayload,
- type,
- oldProps,
- newProps,
- workInProgress,
- childrenUnchanged,
- recyclableInstance,
+ } else {
+ if (!newProps) {
+ invariant(
+ workInProgress.stateNode !== null,
+ 'We must have new props for new mounts. This error is likely ' +
+ 'caused by a bug in React. Please file an issue.',
);
+ // This can happen when we abort work.
+ return null;
+ }
+
+ const currentHostContext = getHostContext();
+ // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on we want to add then top->down or
+ // bottom->up. Top->down is faster in IE11.
+ let wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
if (
- finalizeInitialChildren(
- newInstance,
- type,
- newProps,
+ prepareToHydrateHostInstance(
+ workInProgress,
rootContainerInstance,
currentHostContext,
)
) {
+ // If changes to the hydrated node needs to be applied at the
+ // commit-phase we mark this as such.
markUpdate(workInProgress);
}
- workInProgress.stateNode = newInstance;
- if (childrenUnchanged) {
- // If there are no other effects in this tree, we need to flag this node as having one.
- // Even though we're not going to use it for anything.
- // Otherwise parents won't know that there are new children to propagate upwards.
- markUpdate(workInProgress);
- } else {
- // If children might have changed, we have to add them all to the set.
- appendAllChildren(newInstance, workInProgress);
- }
- }
- };
- updateHostText = function(
- current: Fiber,
- workInProgress: Fiber,
- oldText: string,
- newText: string,
- ) {
- if (oldText !== newText) {
- // If the text content differs, we'll create a new text instance for it.
- const rootContainerInstance = getRootHostContainer();
- const currentHostContext = getHostContext();
- workInProgress.stateNode = createTextInstance(
- newText,
- rootContainerInstance,
- currentHostContext,
- workInProgress,
- );
- // We'll have to mark it as having an effect, even though we won't use the effect for anything.
- // This lets the parents know that at least one of their children has changed.
- markUpdate(workInProgress);
- }
- };
- } else {
- invariant(false, 'Persistent reconciler is disabled.');
- }
- } else {
- if (enableNoopReconciler) {
- // No host operations
- updateHostContainer = function(workInProgress: Fiber) {
- // Noop
- };
- updateHostComponent = function(
- current: Fiber,
- workInProgress: Fiber,
- updatePayload: null | PL,
- type: T,
- oldProps: P,
- newProps: P,
- rootContainerInstance: C,
- currentHostContext: CX,
- ) {
- // Noop
- };
- updateHostText = function(
- current: Fiber,
- workInProgress: Fiber,
- oldText: string,
- newText: string,
- ) {
- // Noop
- };
- } else {
- invariant(false, 'Noop reconciler is disabled.');
- }
- }
-
- function completeWork(
- current: Fiber | null,
- workInProgress: Fiber,
- renderExpirationTime: ExpirationTime,
- ): Fiber | null {
- const newProps = workInProgress.pendingProps;
- switch (workInProgress.tag) {
- case FunctionalComponent:
- return null;
- case ClassComponent: {
- // We are leaving this subtree, so pop context if any.
- popLegacyContextProvider(workInProgress);
- return null;
- }
- case HostRoot: {
- popHostContainer(workInProgress);
- popTopLevelLegacyContextObject(workInProgress);
- const fiberRoot = (workInProgress.stateNode: FiberRoot);
- if (fiberRoot.pendingContext) {
- fiberRoot.context = fiberRoot.pendingContext;
- fiberRoot.pendingContext = null;
- }
- if (current === null || current.child === null) {
- // If we hydrated, pop so that we can delete any remaining children
- // that weren't hydrated.
- popHydrationState(workInProgress);
- // This resets the hacky state to fix isMounted before committing.
- // TODO: Delete this when we delete isMounted and findDOMNode.
- workInProgress.effectTag &= ~Placement;
- }
- updateHostContainer(workInProgress);
- return null;
- }
- case HostComponent: {
- popHostContext(workInProgress);
- const rootContainerInstance = getRootHostContainer();
- const type = workInProgress.type;
- if (current !== null && workInProgress.stateNode != null) {
- // If we have an alternate, that means this is an update and we need to
- // schedule a side-effect to do the updates.
- const oldProps = current.memoizedProps;
- // If we get updated because one of our children updated, we don't
- // have newProps so we'll have to reuse them.
- // TODO: Split the update API as separate for the props vs. children.
- // Even better would be if children weren't special cased at all tho.
- const instance: I = workInProgress.stateNode;
- const currentHostContext = getHostContext();
- // TODO: Experiencing an error where oldProps is null. Suggests a host
- // component is hitting the resume path. Figure out why. Possibly
- // related to `hidden`.
- const updatePayload = prepareUpdate(
- instance,
+ } else {
+ let instance = createInstance(
type,
- oldProps,
newProps,
rootContainerInstance,
currentHostContext,
- );
-
- updateHostComponent(
- current,
workInProgress,
- updatePayload,
- type,
- oldProps,
- newProps,
- rootContainerInstance,
- currentHostContext,
);
- if (current.ref !== workInProgress.ref) {
- markRef(workInProgress);
- }
- } else {
- if (!newProps) {
- invariant(
- workInProgress.stateNode !== null,
- 'We must have new props for new mounts. This error is likely ' +
- 'caused by a bug in React. Please file an issue.',
- );
- // This can happen when we abort work.
- return null;
- }
+ appendAllChildren(instance, workInProgress);
- const currentHostContext = getHostContext();
- // TODO: Move createInstance to beginWork and keep it on a context
- // "stack" as the parent. Then append children as we go in beginWork
- // or completeWork depending on we want to add then top->down or
- // bottom->up. Top->down is faster in IE11.
- let wasHydrated = popHydrationState(workInProgress);
- if (wasHydrated) {
- // TODO: Move this and createInstance step into the beginPhase
- // to consolidate.
- if (
- prepareToHydrateHostInstance(
- workInProgress,
- rootContainerInstance,
- currentHostContext,
- )
- ) {
- // If changes to the hydrated node needs to be applied at the
- // commit-phase we mark this as such.
- markUpdate(workInProgress);
- }
- } else {
- let instance = createInstance(
+ // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
+ if (
+ finalizeInitialChildren(
+ instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
- workInProgress,
- );
-
- appendAllChildren(instance, workInProgress);
-
- // Certain renderers require commit-time effects for initial mount.
- // (eg DOM renderer supports auto-focus for certain elements).
- // Make sure such renderers get scheduled for later work.
- if (
- finalizeInitialChildren(
- instance,
- type,
- newProps,
- rootContainerInstance,
- currentHostContext,
- )
- ) {
- markUpdate(workInProgress);
- }
- workInProgress.stateNode = instance;
+ )
+ ) {
+ markUpdate(workInProgress);
}
+ workInProgress.stateNode = instance;
+ }
- if (workInProgress.ref !== null) {
- // If there is a ref on a host node we need to schedule a callback
- markRef(workInProgress);
- }
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef(workInProgress);
}
- return null;
}
- case HostText: {
- let newText = newProps;
- if (current && workInProgress.stateNode != null) {
- const oldText = current.memoizedProps;
- // If we have an alternate, that means this is an update and we need
- // to schedule a side-effect to do the updates.
- updateHostText(current, workInProgress, oldText, newText);
- } else {
- if (typeof newText !== 'string') {
- invariant(
- workInProgress.stateNode !== null,
- 'We must have new props for new mounts. This error is likely ' +
- 'caused by a bug in React. Please file an issue.',
- );
- // This can happen when we abort work.
- return null;
- }
- const rootContainerInstance = getRootHostContainer();
- const currentHostContext = getHostContext();
- let wasHydrated = popHydrationState(workInProgress);
- if (wasHydrated) {
- if (prepareToHydrateHostTextInstance(workInProgress)) {
- markUpdate(workInProgress);
- }
- } else {
- workInProgress.stateNode = createTextInstance(
- newText,
- rootContainerInstance,
- currentHostContext,
- workInProgress,
- );
+ return null;
+ }
+ case HostText: {
+ let newText = newProps;
+ if (current && workInProgress.stateNode != null) {
+ const oldText = current.memoizedProps;
+ // If we have an alternate, that means this is an update and we need
+ // to schedule a side-effect to do the updates.
+ updateHostText(current, workInProgress, oldText, newText);
+ } else {
+ if (typeof newText !== 'string') {
+ invariant(
+ workInProgress.stateNode !== null,
+ 'We must have new props for new mounts. This error is likely ' +
+ 'caused by a bug in React. Please file an issue.',
+ );
+ // This can happen when we abort work.
+ return null;
+ }
+ const rootContainerInstance = getRootHostContainer();
+ const currentHostContext = getHostContext();
+ let wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
}
+ } else {
+ workInProgress.stateNode = createTextInstance(
+ newText,
+ rootContainerInstance,
+ currentHostContext,
+ workInProgress,
+ );
}
- return null;
}
- case ForwardRef:
- return null;
- case TimeoutComponent:
- return null;
- case Fragment:
- return null;
- case Mode:
- return null;
- case Profiler:
- if (enableProfilerTimer) {
- recordElapsedActualRenderTime(workInProgress);
- }
- return null;
- case HostPortal:
- popHostContainer(workInProgress);
- updateHostContainer(workInProgress);
- return null;
- case ContextProvider:
- // Pop provider fiber
- popProvider(workInProgress);
- return null;
- case ContextConsumer:
- return null;
- // Error cases
- case IndeterminateComponent:
- invariant(
- false,
- 'An indeterminate component should have become determinate before ' +
- 'completing. This error is likely caused by a bug in React. Please ' +
- 'file an issue.',
- );
- // eslint-disable-next-line no-fallthrough
- default:
- invariant(
- false,
- 'Unknown unit of work tag. This error is likely caused by a bug in ' +
- 'React. Please file an issue.',
- );
+ return null;
}
+ case ForwardRef:
+ return null;
+ case TimeoutComponent:
+ return null;
+ case Fragment:
+ return null;
+ case Mode:
+ return null;
+ case Profiler:
+ if (enableProfilerTimer) {
+ recordElapsedActualRenderTime(workInProgress);
+ }
+ return null;
+ case HostPortal:
+ popHostContainer(workInProgress);
+ updateHostContainer(workInProgress);
+ return null;
+ case ContextProvider:
+ // Pop provider fiber
+ popProvider(workInProgress);
+ return null;
+ case ContextConsumer:
+ return null;
+ // Error cases
+ case IndeterminateComponent:
+ invariant(
+ false,
+ 'An indeterminate component should have become determinate before ' +
+ 'completing. This error is likely caused by a bug in React. Please ' +
+ 'file an issue.',
+ );
+ // eslint-disable-next-line no-fallthrough
+ default:
+ invariant(
+ false,
+ 'Unknown unit of work tag. This error is likely caused by a bug in ' +
+ 'React. Please file an issue.',
+ );
}
-
- return {
- completeWork,
- };
}
+
+export {completeWork};
diff --git a/packages/react-reconciler/src/ReactFiberContext.js b/packages/react-reconciler/src/ReactFiberContext.js
index 5db08417035f4..edbe7b2fa50f9 100644
--- a/packages/react-reconciler/src/ReactFiberContext.js
+++ b/packages/react-reconciler/src/ReactFiberContext.js
@@ -8,7 +8,7 @@
*/
import type {Fiber} from './ReactFiber';
-import type {StackCursor, Stack} from './ReactFiberStack';
+import type {StackCursor} from './ReactFiberStack';
import {isFiberMounted} from 'react-reconciler/reflection';
import {ClassComponent, HostRoot} from 'shared/ReactTypeOfWork';
@@ -20,6 +20,7 @@ import checkPropTypes from 'prop-types/checkPropTypes';
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
+import {createCursor, push, pop} from './ReactFiberStack';
let warnedAboutMissingGetChildContext;
@@ -27,308 +28,280 @@ if (__DEV__) {
warnedAboutMissingGetChildContext = {};
}
-export type LegacyContext = {
- getUnmaskedContext(workInProgress: Fiber): Object,
- cacheContext(
- workInProgress: Fiber,
- unmaskedContext: Object,
- maskedContext: Object,
- ): void,
- getMaskedContext(workInProgress: Fiber, unmaskedContext: Object): Object,
- hasContextChanged(): boolean,
- isContextConsumer(fiber: Fiber): boolean,
- isContextProvider(fiber: Fiber): boolean,
- popContextProvider(fiber: Fiber): void,
- popTopLevelContextObject(fiber: Fiber): void,
- pushTopLevelContextObject(
- fiber: Fiber,
- context: Object,
- didChange: boolean,
- ): void,
- processChildContext(fiber: Fiber, parentContext: Object): Object,
- pushContextProvider(workInProgress: Fiber): boolean,
- invalidateContextProvider(workInProgress: Fiber, didChange: boolean): void,
- findCurrentUnmaskedContext(fiber: Fiber): Object,
-};
+// A cursor to the current merged context object on the stack.
+let contextStackCursor: StackCursor