import type {ReactElement} from 'shared/ReactElementType';
import type {
ReactPortal,
Thenable,
ReactContext,
ReactDebugInfo,
} from 'shared/ReactTypes';
import type {Fiber} from './ReactInternalTypes';
import type {Lanes} from './ReactFiberLane';
import type {ThenableState} from './ReactFiberThenable';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
import {
Placement,
ChildDeletion,
Forked,
PlacementDEV,
} from './ReactFiberFlags';
import {NoMode, ConcurrentMode} from './ReactTypeOfMode';
import {
getIteratorFn,
ASYNC_ITERATOR,
REACT_ELEMENT_TYPE,
REACT_FRAGMENT_TYPE,
REACT_PORTAL_TYPE,
REACT_LAZY_TYPE,
REACT_CONTEXT_TYPE,
REACT_LEGACY_ELEMENT_TYPE,
} from 'shared/ReactSymbols';
import {
HostRoot,
HostText,
HostPortal,
Fragment,
FunctionComponent,
} from './ReactWorkTags';
import isArray from 'shared/isArray';
import {
enableRefAsProp,
enableAsyncIterableChildren,
disableLegacyMode,
enableOwnerStacks,
} from 'shared/ReactFeatureFlags';
import {
createWorkInProgress,
resetWorkInProgress,
createFiberFromElement,
createFiberFromFragment,
createFiberFromText,
createFiberFromPortal,
createFiberFromThrow,
} from './ReactFiber';
import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading';
import {getIsHydrating} from './ReactFiberHydrationContext';
import {pushTreeFork} from './ReactFiberTreeContext';
import {
SuspenseException,
createThenableState,
trackUsedThenable,
} from './ReactFiberThenable';
import {readContextDuringReconciliation} from './ReactFiberNewContext';
import {callLazyInitInDEV} from './ReactFiberCallUserSpace';
import {runWithFiberInDEV} from './ReactCurrentFiber';
let thenableState: ThenableState | null = null;
let thenableIndexCounter: number = 0;
let currentDebugInfo: null | ReactDebugInfo = null;
function pushDebugInfo(
debugInfo: null | ReactDebugInfo,
): null | ReactDebugInfo {
if (!__DEV__) {
return null;
}
const previousDebugInfo = currentDebugInfo;
if (debugInfo == null) {
} else if (previousDebugInfo === null) {
currentDebugInfo = debugInfo;
} else {
currentDebugInfo = previousDebugInfo.concat(debugInfo);
}
return previousDebugInfo;
}
let didWarnAboutMaps;
let didWarnAboutGenerators;
let ownerHasKeyUseWarning;
let ownerHasFunctionTypeWarning;
let ownerHasSymbolTypeWarning;
let warnForMissingKey = (
returnFiber: Fiber,
workInProgress: Fiber,
child: mixed,
) => {};
if (__DEV__) {
didWarnAboutMaps = false;
didWarnAboutGenerators = false;
ownerHasKeyUseWarning = ({}: {[string]: boolean});
ownerHasFunctionTypeWarning = ({}: {[string]: boolean});
ownerHasSymbolTypeWarning = ({}: {[string]: boolean});
warnForMissingKey = (
returnFiber: Fiber,
workInProgress: Fiber,
child: mixed,
) => {
if (child === null || typeof child !== 'object') {
return;
}
if (
!child._store ||
((child._store.validated || child.key != null) &&
child._store.validated !== 2)
) {
return;
}
if (typeof child._store !== 'object') {
throw new Error(
'React Component in warnForMissingKey should have a _store. ' +
'This error is likely caused by a bug in React. Please file an issue.',
);
}
child._store.validated = 1;
const componentName = getComponentNameFromFiber(returnFiber);
const componentKey = componentName || 'null';
if (ownerHasKeyUseWarning[componentKey]) {
return;
}
ownerHasKeyUseWarning[componentKey] = true;
const childOwner = child._owner;
const parentOwner = returnFiber._debugOwner;
let currentComponentErrorInfo = '';
if (parentOwner && typeof parentOwner.tag === 'number') {
const name = getComponentNameFromFiber((parentOwner: any));
if (name) {
currentComponentErrorInfo =
'\n\nCheck the render method of `' + name + '`.';
}
}
if (!currentComponentErrorInfo) {
if (componentName) {
currentComponentErrorInfo = `\n\nCheck the top-level render call using <${componentName}>.`;
}
}
let childOwnerAppendix = '';
if (childOwner != null && parentOwner !== childOwner) {
let ownerName = null;
if (typeof childOwner.tag === 'number') {
ownerName = getComponentNameFromFiber((childOwner: any));
} else if (typeof childOwner.name === 'string') {
ownerName = childOwner.name;
}
if (ownerName) {
childOwnerAppendix = ` It was passed a child from ${ownerName}.`;
}
}
runWithFiberInDEV(workInProgress, () => {
console.error(
'Each child in a list should have a unique "key" prop.' +
'%s%s See https://react.dev/link/warning-keys for more information.',
currentComponentErrorInfo,
childOwnerAppendix,
);
});
};
}
function validateFragmentProps(
element: ReactElement,
fiber: null | Fiber,
returnFiber: Fiber,
) {
if (__DEV__) {
const keys = Object.keys(element.props);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key !== 'children' && key !== 'key') {
if (fiber === null) {
fiber = createFiberFromElement(element, returnFiber.mode, 0);
if (__DEV__) {
fiber._debugInfo = currentDebugInfo;
}
fiber.return = returnFiber;
}
runWithFiberInDEV(
fiber,
erroredKey => {
console.error(
'Invalid prop `%s` supplied to `React.Fragment`. ' +
'React.Fragment can only have `key` and `children` props.',
erroredKey,
);
},
key,
);
break;
}
}
if (!enableRefAsProp && element.ref !== null) {
if (fiber === null) {
fiber = createFiberFromElement(element, returnFiber.mode, 0);
if (__DEV__) {
fiber._debugInfo = currentDebugInfo;
}
fiber.return = returnFiber;
}
runWithFiberInDEV(fiber, () => {
console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
});
}
}
}
function unwrapThenable<T>(thenable: Thenable<T>): T {
const index = thenableIndexCounter;
thenableIndexCounter += 1;
if (thenableState === null) {
thenableState = createThenableState();
}
return trackUsedThenable(thenableState, thenable, index);
}
function coerceRef(
returnFiber: Fiber,
current: Fiber | null,
workInProgress: Fiber,
element: ReactElement,
): void {
let ref;
if (enableRefAsProp) {
const refProp = element.props.ref;
ref = refProp !== undefined ? refProp : null;
} else {
ref = element.ref;
}
workInProgress.ref = ref;
}
function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE) {
throw new Error(
'A React Element from an older version of React was rendered. ' +
'This is not supported. It can happen if:\n' +
'- Multiple copies of the "react" package is used.\n' +
'- A library pre-bundled an old copy of "react" or "react/jsx-runtime".\n' +
'- A compiler tries to "inline" JSX instead of using the runtime.',
);
}
const childString = Object.prototype.toString.call(newChild);
throw new Error(
`Objects are not valid as a React child (found: ${
childString === '[object Object]'
? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
: childString
}). ` +
'If you meant to render a collection of children, use an array ' +
'instead.',
);
}
function warnOnFunctionType(returnFiber: Fiber, invalidChild: Function) {
if (__DEV__) {
const parentName = getComponentNameFromFiber(returnFiber) || 'Component';
if (ownerHasFunctionTypeWarning[parentName]) {
return;
}
ownerHasFunctionTypeWarning[parentName] = true;
const name = invalidChild.displayName || invalidChild.name || 'Component';
if (returnFiber.tag === HostRoot) {
console.error(
'Functions are not valid as a React child. This may happen if ' +
'you return %s instead of <%s /> from render. ' +
'Or maybe you meant to call this function rather than return it.\n' +
' root.render(%s)',
name,
name,
name,
);
} else {
console.error(
'Functions are not valid as a React child. This may happen if ' +
'you return %s instead of <%s /> from render. ' +
'Or maybe you meant to call this function rather than return it.\n' +
' <%s>{%s}</%s>',
name,
name,
parentName,
name,
parentName,
);
}
}
}
function warnOnSymbolType(returnFiber: Fiber, invalidChild: symbol) {
if (__DEV__) {
const parentName = getComponentNameFromFiber(returnFiber) || 'Component';
if (ownerHasSymbolTypeWarning[parentName]) {
return;
}
ownerHasSymbolTypeWarning[parentName] = true;
const name = String(invalidChild);
if (returnFiber.tag === HostRoot) {
console.error(
'Symbols are not valid as a React child.\n' + ' root.render(%s)',
name,
);
} else {
console.error(
'Symbols are not valid as a React child.\n' + ' <%s>%s</%s>',
parentName,
name,
parentName,
);
}
}
}
function resolveLazy(lazyType: any) {
if (__DEV__) {
return callLazyInitInDEV(lazyType);
}
const payload = lazyType._payload;
const init = lazyType._init;
return init(payload);
}
type ChildReconciler = (
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
) => Fiber | null;
function createChildReconciler(
shouldTrackSideEffects: boolean,
): ChildReconciler {
function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {
if (!shouldTrackSideEffects) {
return;
}
const deletions = returnFiber.deletions;
if (deletions === null) {
returnFiber.deletions = [childToDelete];
returnFiber.flags |= ChildDeletion;
} else {
deletions.push(childToDelete);
}
}
function deleteRemainingChildren(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
): null {
if (!shouldTrackSideEffects) {
return null;
}
let childToDelete = currentFirstChild;
while (childToDelete !== null) {
deleteChild(returnFiber, childToDelete);
childToDelete = childToDelete.sibling;
}
return null;
}
function mapRemainingChildren(
currentFirstChild: Fiber,
): Map<string | number, Fiber> {
const existingChildren: Map<string | number, Fiber> = new Map();
let existingChild: null | Fiber = currentFirstChild;
while (existingChild !== null) {
if (existingChild.key !== null) {
existingChildren.set(existingChild.key, existingChild);
} else {
existingChildren.set(existingChild.index, existingChild);
}
existingChild = existingChild.sibling;
}
return existingChildren;
}
function useFiber(fiber: Fiber, pendingProps: mixed): Fiber {
const clone = createWorkInProgress(fiber, pendingProps);
clone.index = 0;
clone.sibling = null;
return clone;
}
function placeChild(
newFiber: Fiber,
lastPlacedIndex: number,
newIndex: number,
): number {
newFiber.index = newIndex;
if (!shouldTrackSideEffects) {
newFiber.flags |= Forked;
return lastPlacedIndex;
}
const current = newFiber.alternate;
if (current !== null) {
const oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
newFiber.flags |= Placement | PlacementDEV;
return lastPlacedIndex;
} else {
return oldIndex;
}
} else {
newFiber.flags |= Placement | PlacementDEV;
return lastPlacedIndex;
}
}
function placeSingleChild(newFiber: Fiber): Fiber {
if (shouldTrackSideEffects && newFiber.alternate === null) {
newFiber.flags |= Placement | PlacementDEV;
}
return newFiber;
}
function updateTextNode(
returnFiber: Fiber,
current: Fiber | null,
textContent: string,
lanes: Lanes,
) {
if (current === null || current.tag !== HostText) {
const created = createFiberFromText(textContent, returnFiber.mode, lanes);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
created._debugInfo = currentDebugInfo;
}
return created;
} else {
const existing = useFiber(current, textContent);
existing.return = returnFiber;
if (__DEV__) {
existing._debugInfo = currentDebugInfo;
}
return existing;
}
}
function updateElement(
returnFiber: Fiber,
current: Fiber | null,
element: ReactElement,
lanes: Lanes,
): Fiber {
const elementType = element.type;
if (elementType === REACT_FRAGMENT_TYPE) {
const updated = updateFragment(
returnFiber,
current,
element.props.children,
lanes,
element.key,
);
validateFragmentProps(element, updated, returnFiber);
return updated;
}
if (current !== null) {
if (
current.elementType === elementType ||
(__DEV__
? isCompatibleFamilyForHotReloading(current, element)
: false) ||
(typeof elementType === 'object' &&
elementType !== null &&
elementType.$$typeof === REACT_LAZY_TYPE &&
resolveLazy(elementType) === current.type)
) {
const existing = useFiber(current, element.props);
coerceRef(returnFiber, current, existing, element);
existing.return = returnFiber;
if (__DEV__) {
existing._debugOwner = element._owner;
existing._debugInfo = currentDebugInfo;
}
return existing;
}
}
const created = createFiberFromElement(element, returnFiber.mode, lanes);
coerceRef(returnFiber, current, created, element);
created.return = returnFiber;
if (__DEV__) {
created._debugInfo = currentDebugInfo;
}
return created;
}
function updatePortal(
returnFiber: Fiber,
current: Fiber | null,
portal: ReactPortal,
lanes: Lanes,
): Fiber {
if (
current === null ||
current.tag !== HostPortal ||
current.stateNode.containerInfo !== portal.containerInfo ||
current.stateNode.implementation !== portal.implementation
) {
const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
created.return = returnFiber;
if (__DEV__) {
created._debugInfo = currentDebugInfo;
}
return created;
} else {
const existing = useFiber(current, portal.children || []);
existing.return = returnFiber;
if (__DEV__) {
existing._debugInfo = currentDebugInfo;
}
return existing;
}
}
function updateFragment(
returnFiber: Fiber,
current: Fiber | null,
fragment: Iterable<React$Node>,
lanes: Lanes,
key: null | string,
): Fiber {
if (current === null || current.tag !== Fragment) {
const created = createFiberFromFragment(
fragment,
returnFiber.mode,
lanes,
key,
);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
created._debugInfo = currentDebugInfo;
}
return created;
} else {
const existing = useFiber(current, fragment);
existing.return = returnFiber;
if (__DEV__) {
existing._debugInfo = currentDebugInfo;
}
return existing;
}
}
function createChild(
returnFiber: Fiber,
newChild: any,
lanes: Lanes,
): Fiber | null {
if (
(typeof newChild === 'string' && newChild !== '') ||
typeof newChild === 'number' ||
typeof newChild === 'bigint'
) {
const created = createFiberFromText(
'' + newChild,
returnFiber.mode,
lanes,
);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
created._debugInfo = currentDebugInfo;
}
return created;
}
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
const created = createFiberFromElement(
newChild,
returnFiber.mode,
lanes,
);
coerceRef(returnFiber, null, created, newChild);
created.return = returnFiber;
if (__DEV__) {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
created._debugInfo = currentDebugInfo;
currentDebugInfo = prevDebugInfo;
}
return created;
}
case REACT_PORTAL_TYPE: {
const created = createFiberFromPortal(
newChild,
returnFiber.mode,
lanes,
);
created.return = returnFiber;
if (__DEV__) {
created._debugInfo = currentDebugInfo;
}
return created;
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const created = createChild(returnFiber, resolvedChild, lanes);
currentDebugInfo = prevDebugInfo;
return created;
}
}
if (
isArray(newChild) ||
getIteratorFn(newChild) ||
(enableAsyncIterableChildren &&
typeof newChild[ASYNC_ITERATOR] === 'function')
) {
const created = createFiberFromFragment(
newChild,
returnFiber.mode,
lanes,
null,
);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
created._debugInfo = currentDebugInfo;
currentDebugInfo = prevDebugInfo;
}
return created;
}
if (typeof newChild.then === 'function') {
const thenable: Thenable<any> = (newChild: any);
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const created = createChild(
returnFiber,
unwrapThenable(thenable),
lanes,
);
currentDebugInfo = prevDebugInfo;
return created;
}
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
const context: ReactContext<mixed> = (newChild: any);
return createChild(
returnFiber,
readContextDuringReconciliation(returnFiber, context, lanes),
lanes,
);
}
throwOnInvalidObjectType(returnFiber, newChild);
}
if (__DEV__) {
if (typeof newChild === 'function') {
warnOnFunctionType(returnFiber, newChild);
}
if (typeof newChild === 'symbol') {
warnOnSymbolType(returnFiber, newChild);
}
}
return null;
}
function updateSlot(
returnFiber: Fiber,
oldFiber: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
const key = oldFiber !== null ? oldFiber.key : null;
if (
(typeof newChild === 'string' && newChild !== '') ||
typeof newChild === 'number' ||
typeof newChild === 'bigint'
) {
if (key !== null) {
return null;
}
return updateTextNode(
returnFiber,
oldFiber,
'' + newChild,
lanes,
);
}
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
if (newChild.key === key) {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const updated = updateElement(
returnFiber,
oldFiber,
newChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
} else {
return null;
}
}
case REACT_PORTAL_TYPE: {
if (newChild.key === key) {
return updatePortal(returnFiber, oldFiber, newChild, lanes);
} else {
return null;
}
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const updated = updateSlot(
returnFiber,
oldFiber,
resolvedChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
}
if (
isArray(newChild) ||
getIteratorFn(newChild) ||
(enableAsyncIterableChildren &&
typeof newChild[ASYNC_ITERATOR] === 'function')
) {
if (key !== null) {
return null;
}
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const updated = updateFragment(
returnFiber,
oldFiber,
newChild,
lanes,
null,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
if (typeof newChild.then === 'function') {
const thenable: Thenable<any> = (newChild: any);
const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
const updated = updateSlot(
returnFiber,
oldFiber,
unwrapThenable(thenable),
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
const context: ReactContext<mixed> = (newChild: any);
return updateSlot(
returnFiber,
oldFiber,
readContextDuringReconciliation(returnFiber, context, lanes),
lanes,
);
}
throwOnInvalidObjectType(returnFiber, newChild);
}
if (__DEV__) {
if (typeof newChild === 'function') {
warnOnFunctionType(returnFiber, newChild);
}
if (typeof newChild === 'symbol') {
warnOnSymbolType(returnFiber, newChild);
}
}
return null;
}
function updateFromMap(
existingChildren: Map<string | number, Fiber>,
returnFiber: Fiber,
newIdx: number,
newChild: any,
lanes: Lanes,
): Fiber | null {
if (
(typeof newChild === 'string' && newChild !== '') ||
typeof newChild === 'number' ||
typeof newChild === 'bigint'
) {
const matchedFiber = existingChildren.get(newIdx) || null;
return updateTextNode(
returnFiber,
matchedFiber,
'' + newChild,
lanes,
);
}
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
const matchedFiber =
existingChildren.get(
newChild.key === null ? newIdx : newChild.key,
) || null;
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const updated = updateElement(
returnFiber,
matchedFiber,
newChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
case REACT_PORTAL_TYPE: {
const matchedFiber =
existingChildren.get(
newChild.key === null ? newIdx : newChild.key,
) || null;
return updatePortal(returnFiber, matchedFiber, newChild, lanes);
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const updated = updateFromMap(
existingChildren,
returnFiber,
newIdx,
resolvedChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
}
if (
isArray(newChild) ||
getIteratorFn(newChild) ||
(enableAsyncIterableChildren &&
typeof newChild[ASYNC_ITERATOR] === 'function')
) {
const matchedFiber = existingChildren.get(newIdx) || null;
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const updated = updateFragment(
returnFiber,
matchedFiber,
newChild,
lanes,
null,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
if (typeof newChild.then === 'function') {
const thenable: Thenable<any> = (newChild: any);
const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
const updated = updateFromMap(
existingChildren,
returnFiber,
newIdx,
unwrapThenable(thenable),
lanes,
);
currentDebugInfo = prevDebugInfo;
return updated;
}
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
const context: ReactContext<mixed> = (newChild: any);
return updateFromMap(
existingChildren,
returnFiber,
newIdx,
readContextDuringReconciliation(returnFiber, context, lanes),
lanes,
);
}
throwOnInvalidObjectType(returnFiber, newChild);
}
if (__DEV__) {
if (typeof newChild === 'function') {
warnOnFunctionType(returnFiber, newChild);
}
if (typeof newChild === 'symbol') {
warnOnSymbolType(returnFiber, newChild);
}
}
return null;
}
function warnOnInvalidKey(
returnFiber: Fiber,
workInProgress: Fiber,
child: mixed,
knownKeys: Set<string> | null,
): Set<string> | null {
if (__DEV__) {
if (typeof child !== 'object' || child === null) {
return knownKeys;
}
switch (child.$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
warnForMissingKey(returnFiber, workInProgress, child);
const key = child.key;
if (typeof key !== 'string') {
break;
}
if (knownKeys === null) {
knownKeys = new Set();
knownKeys.add(key);
break;
}
if (!knownKeys.has(key)) {
knownKeys.add(key);
break;
}
runWithFiberInDEV(workInProgress, () => {
console.error(
'Encountered two children with the same key, `%s`. ' +
'Keys should be unique so that components maintain their identity ' +
'across updates. Non-unique keys may cause children to be ' +
'duplicated and/or omitted — the behavior is unsupported and ' +
'could change in a future version.',
key,
);
});
break;
case REACT_LAZY_TYPE: {
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV((child: any));
} else {
const payload = child._payload;
const init = (child._init: any);
resolvedChild = init(payload);
}
warnOnInvalidKey(
returnFiber,
workInProgress,
resolvedChild,
knownKeys,
);
break;
}
default:
break;
}
}
return knownKeys;
}
function reconcileChildrenArray(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: Array<any>,
lanes: Lanes,
): Fiber | null {
let knownKeys: Set<string> | null = null;
let resultingFirstChild: Fiber | null = null;
let previousNewFiber: Fiber | null = null;
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
if (oldFiber.index > newIdx) {
nextOldFiber = oldFiber;
oldFiber = null;
} else {
nextOldFiber = oldFiber.sibling;
}
const newFiber = updateSlot(
returnFiber,
oldFiber,
newChildren[newIdx],
lanes,
);
if (newFiber === null) {
if (oldFiber === null) {
oldFiber = nextOldFiber;
}
break;
}
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
newChildren[newIdx],
knownKeys,
);
}
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
if (newIdx === newChildren.length) {
deleteRemainingChildren(returnFiber, oldFiber);
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
if (oldFiber === null) {
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
if (newFiber === null) {
continue;
}
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
newChildren[newIdx],
knownKeys,
);
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
const existingChildren = mapRemainingChildren(oldFiber);
for (; newIdx < newChildren.length; newIdx++) {
const newFiber = updateFromMap(
existingChildren,
returnFiber,
newIdx,
newChildren[newIdx],
lanes,
);
if (newFiber !== null) {
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
newChildren[newIdx],
knownKeys,
);
}
if (shouldTrackSideEffects) {
if (newFiber.alternate !== null) {
existingChildren.delete(
newFiber.key === null ? newIdx : newFiber.key,
);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
}
if (shouldTrackSideEffects) {
existingChildren.forEach(child => deleteChild(returnFiber, child));
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
function reconcileChildrenIteratable(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildrenIterable: Iterable<mixed>,
lanes: Lanes,
): Fiber | null {
const iteratorFn = getIteratorFn(newChildrenIterable);
if (typeof iteratorFn !== 'function') {
throw new Error(
'An object is not an iterable. This error is likely caused by a bug in ' +
'React. Please file an issue.',
);
}
const newChildren = iteratorFn.call(newChildrenIterable);
if (__DEV__) {
if (newChildren === newChildrenIterable) {
const isGeneratorComponent =
returnFiber.tag === FunctionComponent &&
Object.prototype.toString.call(returnFiber.type) ===
'[object GeneratorFunction]' &&
Object.prototype.toString.call(newChildren) === '[object Generator]';
if (!isGeneratorComponent) {
if (!didWarnAboutGenerators) {
console.error(
'Using Iterators as children is unsupported and will likely yield ' +
'unexpected results because enumerating a generator mutates it. ' +
'You may convert it to an array with `Array.from()` or the ' +
'`[...spread]` operator before rendering. You can also use an ' +
'Iterable that can iterate multiple times over the same items.',
);
}
didWarnAboutGenerators = true;
}
} else if ((newChildrenIterable: any).entries === iteratorFn) {
if (!didWarnAboutMaps) {
console.error(
'Using Maps as children is not supported. ' +
'Use an array of keyed ReactElements instead.',
);
didWarnAboutMaps = true;
}
}
}
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
newChildren,
lanes,
);
}
function reconcileChildrenAsyncIteratable(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildrenIterable: AsyncIterable<mixed>,
lanes: Lanes,
): Fiber | null {
const newChildren = newChildrenIterable[ASYNC_ITERATOR]();
if (__DEV__) {
if (newChildren === newChildrenIterable) {
const isGeneratorComponent =
returnFiber.tag === FunctionComponent &&
Object.prototype.toString.call(returnFiber.type) ===
'[object AsyncGeneratorFunction]' &&
Object.prototype.toString.call(newChildren) ===
'[object AsyncGenerator]';
if (!isGeneratorComponent) {
if (!didWarnAboutGenerators) {
console.error(
'Using AsyncIterators as children is unsupported and will likely yield ' +
'unexpected results because enumerating a generator mutates it. ' +
'You can use an AsyncIterable that can iterate multiple times over ' +
'the same items.',
);
}
didWarnAboutGenerators = true;
}
}
}
if (newChildren == null) {
throw new Error('An iterable object provided no iterator.');
}
const iterator: Iterator<mixed> = ({
next(): IteratorResult<mixed, void> {
return unwrapThenable(newChildren.next());
},
}: any);
return reconcileChildrenIterator(
returnFiber,
currentFirstChild,
iterator,
lanes,
);
}
function reconcileChildrenIterator(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: ?Iterator<mixed>,
lanes: Lanes,
): Fiber | null {
if (newChildren == null) {
throw new Error('An iterable object provided no iterator.');
}
let resultingFirstChild: Fiber | null = null;
let previousNewFiber: Fiber | null = null;
let oldFiber = currentFirstChild;
let lastPlacedIndex = 0;
let newIdx = 0;
let nextOldFiber = null;
let knownKeys: Set<string> | null = null;
let step = newChildren.next();
for (
;
oldFiber !== null && !step.done;
newIdx++, step = newChildren.next()
) {
if (oldFiber.index > newIdx) {
nextOldFiber = oldFiber;
oldFiber = null;
} else {
nextOldFiber = oldFiber.sibling;
}
const newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
if (newFiber === null) {
if (oldFiber === null) {
oldFiber = nextOldFiber;
}
break;
}
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
step.value,
knownKeys,
);
}
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
oldFiber = nextOldFiber;
}
if (step.done) {
deleteRemainingChildren(returnFiber, oldFiber);
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
if (oldFiber === null) {
for (; !step.done; newIdx++, step = newChildren.next()) {
const newFiber = createChild(returnFiber, step.value, lanes);
if (newFiber === null) {
continue;
}
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
step.value,
knownKeys,
);
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
const existingChildren = mapRemainingChildren(oldFiber);
for (; !step.done; newIdx++, step = newChildren.next()) {
const newFiber = updateFromMap(
existingChildren,
returnFiber,
newIdx,
step.value,
lanes,
);
if (newFiber !== null) {
if (__DEV__) {
knownKeys = warnOnInvalidKey(
returnFiber,
newFiber,
step.value,
knownKeys,
);
}
if (shouldTrackSideEffects) {
if (newFiber.alternate !== null) {
existingChildren.delete(
newFiber.key === null ? newIdx : newFiber.key,
);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
if (previousNewFiber === null) {
resultingFirstChild = newFiber;
} else {
previousNewFiber.sibling = newFiber;
}
previousNewFiber = newFiber;
}
}
if (shouldTrackSideEffects) {
existingChildren.forEach(child => deleteChild(returnFiber, child));
}
if (getIsHydrating()) {
const numberOfForks = newIdx;
pushTreeFork(returnFiber, numberOfForks);
}
return resultingFirstChild;
}
function reconcileSingleTextNode(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
textContent: string,
lanes: Lanes,
): Fiber {
if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
const existing = useFiber(currentFirstChild, textContent);
existing.return = returnFiber;
return existing;
}
deleteRemainingChildren(returnFiber, currentFirstChild);
const created = createFiberFromText(textContent, returnFiber.mode, lanes);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
created._debugInfo = currentDebugInfo;
}
return created;
}
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement,
lanes: Lanes,
): Fiber {
const key = element.key;
let child = currentFirstChild;
while (child !== null) {
if (child.key === key) {
const elementType = element.type;
if (elementType === REACT_FRAGMENT_TYPE) {
if (child.tag === Fragment) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props.children);
existing.return = returnFiber;
if (__DEV__) {
existing._debugOwner = element._owner;
existing._debugInfo = currentDebugInfo;
}
validateFragmentProps(element, existing, returnFiber);
return existing;
}
} else {
if (
child.elementType === elementType ||
(__DEV__
? isCompatibleFamilyForHotReloading(child, element)
: false) ||
(typeof elementType === 'object' &&
elementType !== null &&
elementType.$$typeof === REACT_LAZY_TYPE &&
resolveLazy(elementType) === child.type)
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
coerceRef(returnFiber, child, existing, element);
existing.return = returnFiber;
if (__DEV__) {
existing._debugOwner = element._owner;
existing._debugInfo = currentDebugInfo;
}
return existing;
}
}
deleteRemainingChildren(returnFiber, child);
break;
} else {
deleteChild(returnFiber, child);
}
child = child.sibling;
}
if (element.type === REACT_FRAGMENT_TYPE) {
const created = createFiberFromFragment(
element.props.children,
returnFiber.mode,
lanes,
element.key,
);
created.return = returnFiber;
if (__DEV__) {
created._debugOwner = returnFiber;
if (enableOwnerStacks) {
created._debugTask = returnFiber._debugTask;
}
created._debugInfo = currentDebugInfo;
}
validateFragmentProps(element, created, returnFiber);
return created;
} else {
const created = createFiberFromElement(element, returnFiber.mode, lanes);
coerceRef(returnFiber, currentFirstChild, created, element);
created.return = returnFiber;
if (__DEV__) {
created._debugInfo = currentDebugInfo;
}
return created;
}
}
function reconcileSinglePortal(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
portal: ReactPortal,
lanes: Lanes,
): Fiber {
const key = portal.key;
let child = currentFirstChild;
while (child !== null) {
if (child.key === key) {
if (
child.tag === HostPortal &&
child.stateNode.containerInfo === portal.containerInfo &&
child.stateNode.implementation === portal.implementation
) {
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, portal.children || []);
existing.return = returnFiber;
return existing;
} else {
deleteRemainingChildren(returnFiber, child);
break;
}
} else {
deleteChild(returnFiber, child);
}
child = child.sibling;
}
const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
created.return = returnFiber;
return created;
}
function reconcileChildFibersImpl(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
const isUnkeyedTopLevelFragment =
typeof newChild === 'object' &&
newChild !== null &&
newChild.type === REACT_FRAGMENT_TYPE &&
newChild.key === null;
if (isUnkeyedTopLevelFragment) {
validateFragmentProps(newChild, null, returnFiber);
newChild = newChild.props.children;
}
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const firstChild = placeSingleChild(
reconcileSingleElement(
returnFiber,
currentFirstChild,
newChild,
lanes,
),
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
case REACT_PORTAL_TYPE:
return placeSingleChild(
reconcileSinglePortal(
returnFiber,
currentFirstChild,
newChild,
lanes,
),
);
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let result;
if (__DEV__) {
result = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
result = init(payload);
}
const firstChild = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
result,
lanes,
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
}
if (isArray(newChild)) {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const firstChild = reconcileChildrenArray(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
if (getIteratorFn(newChild)) {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const firstChild = reconcileChildrenIteratable(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
if (
enableAsyncIterableChildren &&
typeof newChild[ASYNC_ITERATOR] === 'function'
) {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
const firstChild = reconcileChildrenAsyncIteratable(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
if (typeof newChild.then === 'function') {
const thenable: Thenable<any> = (newChild: any);
const prevDebugInfo = pushDebugInfo((thenable: any)._debugInfo);
const firstChild = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
unwrapThenable(thenable),
lanes,
);
currentDebugInfo = prevDebugInfo;
return firstChild;
}
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
const context: ReactContext<mixed> = (newChild: any);
return reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
readContextDuringReconciliation(returnFiber, context, lanes),
lanes,
);
}
throwOnInvalidObjectType(returnFiber, newChild);
}
if (
(typeof newChild === 'string' && newChild !== '') ||
typeof newChild === 'number' ||
typeof newChild === 'bigint'
) {
return placeSingleChild(
reconcileSingleTextNode(
returnFiber,
currentFirstChild,
'' + newChild,
lanes,
),
);
}
if (__DEV__) {
if (typeof newChild === 'function') {
warnOnFunctionType(returnFiber, newChild);
}
if (typeof newChild === 'symbol') {
warnOnSymbolType(returnFiber, newChild);
}
}
return deleteRemainingChildren(returnFiber, currentFirstChild);
}
function reconcileChildFibers(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChild: any,
lanes: Lanes,
): Fiber | null {
const prevDebugInfo = currentDebugInfo;
currentDebugInfo = null;
try {
thenableIndexCounter = 0;
const firstChildFiber = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
newChild,
lanes,
);
thenableState = null;
return firstChildFiber;
} catch (x) {
if (
x === SuspenseException ||
(!disableLegacyMode &&
(returnFiber.mode & ConcurrentMode) === NoMode &&
typeof x === 'object' &&
x !== null &&
typeof x.then === 'function')
) {
throw x;
}
const throwFiber = createFiberFromThrow(x, returnFiber.mode, lanes);
throwFiber.return = returnFiber;
if (__DEV__) {
const debugInfo = (throwFiber._debugInfo = currentDebugInfo);
throwFiber._debugOwner = returnFiber._debugOwner;
if (enableOwnerStacks) {
throwFiber._debugTask = returnFiber._debugTask;
}
if (debugInfo != null) {
for (let i = debugInfo.length - 1; i >= 0; i--) {
if (typeof debugInfo[i].stack === 'string') {
throwFiber._debugOwner = (debugInfo[i]: any);
if (enableOwnerStacks) {
throwFiber._debugTask = debugInfo[i].debugTask;
}
break;
}
}
}
}
return throwFiber;
} finally {
currentDebugInfo = prevDebugInfo;
}
}
return reconcileChildFibers;
}
export const reconcileChildFibers: ChildReconciler =
createChildReconciler(true);
export const mountChildFibers: ChildReconciler = createChildReconciler(false);
export function resetChildReconcilerOnUnwind(): void {
thenableState = null;
thenableIndexCounter = 0;
}
export function cloneChildFibers(
current: Fiber | null,
workInProgress: Fiber,
): void {
if (current !== null && workInProgress.child !== current.child) {
throw new Error('Resuming work not yet implemented.');
}
if (workInProgress.child === null) {
return;
}
let currentChild = workInProgress.child;
let newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
workInProgress.child = newChild;
newChild.return = workInProgress;
while (currentChild.sibling !== null) {
currentChild = currentChild.sibling;
newChild = newChild.sibling = createWorkInProgress(
currentChild,
currentChild.pendingProps,
);
newChild.return = workInProgress;
}
newChild.sibling = null;
}
export function resetChildFibers(workInProgress: Fiber, lanes: Lanes): void {
let child = workInProgress.child;
while (child !== null) {
resetWorkInProgress(child, lanes);
child = child.sibling;
}
}