1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {ReactElement} from 'shared/ReactElementType';
    
  11. import type {ReactPortal, Thenable, ReactContext} from 'shared/ReactTypes';
    
  12. import type {Fiber} from './ReactInternalTypes';
    
  13. import type {Lanes} from './ReactFiberLane';
    
  14. import type {ThenableState} from './ReactFiberThenable';
    
  15. 
    
  16. import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
    
  17. import {
    
  18.   Placement,
    
  19.   ChildDeletion,
    
  20.   Forked,
    
  21.   PlacementDEV,
    
  22. } from './ReactFiberFlags';
    
  23. import {
    
  24.   getIteratorFn,
    
  25.   REACT_ELEMENT_TYPE,
    
  26.   REACT_FRAGMENT_TYPE,
    
  27.   REACT_PORTAL_TYPE,
    
  28.   REACT_LAZY_TYPE,
    
  29.   REACT_CONTEXT_TYPE,
    
  30.   REACT_SERVER_CONTEXT_TYPE,
    
  31. } from 'shared/ReactSymbols';
    
  32. import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
    
  33. import isArray from 'shared/isArray';
    
  34. import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
    
  35. 
    
  36. import {
    
  37.   createWorkInProgress,
    
  38.   resetWorkInProgress,
    
  39.   createFiberFromElement,
    
  40.   createFiberFromFragment,
    
  41.   createFiberFromText,
    
  42.   createFiberFromPortal,
    
  43. } from './ReactFiber';
    
  44. import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading';
    
  45. import {getIsHydrating} from './ReactFiberHydrationContext';
    
  46. import {pushTreeFork} from './ReactFiberTreeContext';
    
  47. import {createThenableState, trackUsedThenable} from './ReactFiberThenable';
    
  48. import {readContextDuringReconcilation} from './ReactFiberNewContext';
    
  49. 
    
  50. // This tracks the thenables that are unwrapped during reconcilation.
    
  51. let thenableState: ThenableState | null = null;
    
  52. let thenableIndexCounter: number = 0;
    
  53. 
    
  54. let didWarnAboutMaps;
    
  55. let didWarnAboutGenerators;
    
  56. let didWarnAboutStringRefs;
    
  57. let ownerHasKeyUseWarning;
    
  58. let ownerHasFunctionTypeWarning;
    
  59. let warnForMissingKey = (child: mixed, returnFiber: Fiber) => {};
    
  60. 
    
  61. if (__DEV__) {
    
  62.   didWarnAboutMaps = false;
    
  63.   didWarnAboutGenerators = false;
    
  64.   didWarnAboutStringRefs = ({}: {[string]: boolean});
    
  65. 
    
  66.   /**
    
  67.    * Warn if there's no key explicitly set on dynamic arrays of children or
    
  68.    * object keys are not valid. This allows us to keep track of children between
    
  69.    * updates.
    
  70.    */
    
  71.   ownerHasKeyUseWarning = ({}: {[string]: boolean});
    
  72.   ownerHasFunctionTypeWarning = ({}: {[string]: boolean});
    
  73. 
    
  74.   warnForMissingKey = (child: mixed, returnFiber: Fiber) => {
    
  75.     if (child === null || typeof child !== 'object') {
    
  76.       return;
    
  77.     }
    
  78.     if (!child._store || child._store.validated || child.key != null) {
    
  79.       return;
    
  80.     }
    
  81. 
    
  82.     if (typeof child._store !== 'object') {
    
  83.       throw new Error(
    
  84.         'React Component in warnForMissingKey should have a _store. ' +
    
  85.           'This error is likely caused by a bug in React. Please file an issue.',
    
  86.       );
    
  87.     }
    
  88. 
    
  89.     // $FlowFixMe[cannot-write] unable to narrow type from mixed to writable object
    
  90.     child._store.validated = true;
    
  91. 
    
  92.     const componentName = getComponentNameFromFiber(returnFiber) || 'Component';
    
  93. 
    
  94.     if (ownerHasKeyUseWarning[componentName]) {
    
  95.       return;
    
  96.     }
    
  97.     ownerHasKeyUseWarning[componentName] = true;
    
  98. 
    
  99.     console.error(
    
  100.       'Each child in a list should have a unique ' +
    
  101.         '"key" prop. See https://reactjs.org/link/warning-keys for ' +
    
  102.         'more information.',
    
  103.     );
    
  104.   };
    
  105. }
    
  106. 
    
  107. function isReactClass(type: any) {
    
  108.   return type.prototype && type.prototype.isReactComponent;
    
  109. }
    
  110. 
    
  111. function unwrapThenable<T>(thenable: Thenable<T>): T {
    
  112.   const index = thenableIndexCounter;
    
  113.   thenableIndexCounter += 1;
    
  114.   if (thenableState === null) {
    
  115.     thenableState = createThenableState();
    
  116.   }
    
  117.   return trackUsedThenable(thenableState, thenable, index);
    
  118. }
    
  119. 
    
  120. function coerceRef(
    
  121.   returnFiber: Fiber,
    
  122.   current: Fiber | null,
    
  123.   element: ReactElement,
    
  124. ) {
    
  125.   const mixedRef = element.ref;
    
  126.   if (
    
  127.     mixedRef !== null &&
    
  128.     typeof mixedRef !== 'function' &&
    
  129.     typeof mixedRef !== 'object'
    
  130.   ) {
    
  131.     if (__DEV__) {
    
  132.       if (
    
  133.         // We warn in ReactElement.js if owner and self are equal for string refs
    
  134.         // because these cannot be automatically converted to an arrow function
    
  135.         // using a codemod. Therefore, we don't have to warn about string refs again.
    
  136.         !(
    
  137.           element._owner &&
    
  138.           element._self &&
    
  139.           element._owner.stateNode !== element._self
    
  140.         ) &&
    
  141.         // Will already throw with "Function components cannot have string refs"
    
  142.         !(
    
  143.           element._owner &&
    
  144.           ((element._owner: any): Fiber).tag !== ClassComponent
    
  145.         ) &&
    
  146.         // Will already warn with "Function components cannot be given refs"
    
  147.         !(typeof element.type === 'function' && !isReactClass(element.type)) &&
    
  148.         // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
    
  149.         element._owner
    
  150.       ) {
    
  151.         const componentName =
    
  152.           getComponentNameFromFiber(returnFiber) || 'Component';
    
  153.         if (!didWarnAboutStringRefs[componentName]) {
    
  154.           console.error(
    
  155.             'Component "%s" contains the string ref "%s". Support for string refs ' +
    
  156.               'will be removed in a future major release. We recommend using ' +
    
  157.               'useRef() or createRef() instead. ' +
    
  158.               'Learn more about using refs safely here: ' +
    
  159.               'https://reactjs.org/link/strict-mode-string-ref',
    
  160.             componentName,
    
  161.             mixedRef,
    
  162.           );
    
  163.           didWarnAboutStringRefs[componentName] = true;
    
  164.         }
    
  165.       }
    
  166.     }
    
  167. 
    
  168.     if (element._owner) {
    
  169.       const owner: ?Fiber = (element._owner: any);
    
  170.       let inst;
    
  171.       if (owner) {
    
  172.         const ownerFiber = ((owner: any): Fiber);
    
  173. 
    
  174.         if (ownerFiber.tag !== ClassComponent) {
    
  175.           throw new Error(
    
  176.             'Function components cannot have string refs. ' +
    
  177.               'We recommend using useRef() instead. ' +
    
  178.               'Learn more about using refs safely here: ' +
    
  179.               'https://reactjs.org/link/strict-mode-string-ref',
    
  180.           );
    
  181.         }
    
  182. 
    
  183.         inst = ownerFiber.stateNode;
    
  184.       }
    
  185. 
    
  186.       if (!inst) {
    
  187.         throw new Error(
    
  188.           `Missing owner for string ref ${mixedRef}. This error is likely caused by a ` +
    
  189.             'bug in React. Please file an issue.',
    
  190.         );
    
  191.       }
    
  192.       // Assigning this to a const so Flow knows it won't change in the closure
    
  193.       const resolvedInst = inst;
    
  194. 
    
  195.       if (__DEV__) {
    
  196.         checkPropStringCoercion(mixedRef, 'ref');
    
  197.       }
    
  198.       const stringRef = '' + mixedRef;
    
  199.       // Check if previous string ref matches new string ref
    
  200.       if (
    
  201.         current !== null &&
    
  202.         current.ref !== null &&
    
  203.         typeof current.ref === 'function' &&
    
  204.         current.ref._stringRef === stringRef
    
  205.       ) {
    
  206.         return current.ref;
    
  207.       }
    
  208.       const ref = function (value: mixed) {
    
  209.         const refs = resolvedInst.refs;
    
  210.         if (value === null) {
    
  211.           delete refs[stringRef];
    
  212.         } else {
    
  213.           refs[stringRef] = value;
    
  214.         }
    
  215.       };
    
  216.       ref._stringRef = stringRef;
    
  217.       return ref;
    
  218.     } else {
    
  219.       if (typeof mixedRef !== 'string') {
    
  220.         throw new Error(
    
  221.           'Expected ref to be a function, a string, an object returned by React.createRef(), or null.',
    
  222.         );
    
  223.       }
    
  224. 
    
  225.       if (!element._owner) {
    
  226.         throw new Error(
    
  227.           `Element ref was specified as a string (${mixedRef}) but no owner was set. This could happen for one of` +
    
  228.             ' the following reasons:\n' +
    
  229.             '1. You may be adding a ref to a function component\n' +
    
  230.             "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
    
  231.             '3. You have multiple copies of React loaded\n' +
    
  232.             'See https://reactjs.org/link/refs-must-have-owner for more information.',
    
  233.         );
    
  234.       }
    
  235.     }
    
  236.   }
    
  237.   return mixedRef;
    
  238. }
    
  239. 
    
  240. function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
    
  241.   // $FlowFixMe[method-unbinding]
    
  242.   const childString = Object.prototype.toString.call(newChild);
    
  243. 
    
  244.   throw new Error(
    
  245.     `Objects are not valid as a React child (found: ${
    
  246.       childString === '[object Object]'
    
  247.         ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'
    
  248.         : childString
    
  249.     }). ` +
    
  250.       'If you meant to render a collection of children, use an array ' +
    
  251.       'instead.',
    
  252.   );
    
  253. }
    
  254. 
    
  255. function warnOnFunctionType(returnFiber: Fiber) {
    
  256.   if (__DEV__) {
    
  257.     const componentName = getComponentNameFromFiber(returnFiber) || 'Component';
    
  258. 
    
  259.     if (ownerHasFunctionTypeWarning[componentName]) {
    
  260.       return;
    
  261.     }
    
  262.     ownerHasFunctionTypeWarning[componentName] = true;
    
  263. 
    
  264.     console.error(
    
  265.       'Functions are not valid as a React child. This may happen if ' +
    
  266.         'you return a Component instead of <Component /> from render. ' +
    
  267.         'Or maybe you meant to call this function rather than return it.',
    
  268.     );
    
  269.   }
    
  270. }
    
  271. 
    
  272. function resolveLazy(lazyType: any) {
    
  273.   const payload = lazyType._payload;
    
  274.   const init = lazyType._init;
    
  275.   return init(payload);
    
  276. }
    
  277. 
    
  278. type ChildReconciler = (
    
  279.   returnFiber: Fiber,
    
  280.   currentFirstChild: Fiber | null,
    
  281.   newChild: any,
    
  282.   lanes: Lanes,
    
  283. ) => Fiber | null;
    
  284. 
    
  285. // This wrapper function exists because I expect to clone the code in each path
    
  286. // to be able to optimize each path individually by branching early. This needs
    
  287. // a compiler or we can do it manually. Helpers that don't need this branching
    
  288. // live outside of this function.
    
  289. function createChildReconciler(
    
  290.   shouldTrackSideEffects: boolean,
    
  291. ): ChildReconciler {
    
  292.   function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {
    
  293.     if (!shouldTrackSideEffects) {
    
  294.       // Noop.
    
  295.       return;
    
  296.     }
    
  297.     const deletions = returnFiber.deletions;
    
  298.     if (deletions === null) {
    
  299.       returnFiber.deletions = [childToDelete];
    
  300.       returnFiber.flags |= ChildDeletion;
    
  301.     } else {
    
  302.       deletions.push(childToDelete);
    
  303.     }
    
  304.   }
    
  305. 
    
  306.   function deleteRemainingChildren(
    
  307.     returnFiber: Fiber,
    
  308.     currentFirstChild: Fiber | null,
    
  309.   ): null {
    
  310.     if (!shouldTrackSideEffects) {
    
  311.       // Noop.
    
  312.       return null;
    
  313.     }
    
  314. 
    
  315.     // TODO: For the shouldClone case, this could be micro-optimized a bit by
    
  316.     // assuming that after the first child we've already added everything.
    
  317.     let childToDelete = currentFirstChild;
    
  318.     while (childToDelete !== null) {
    
  319.       deleteChild(returnFiber, childToDelete);
    
  320.       childToDelete = childToDelete.sibling;
    
  321.     }
    
  322.     return null;
    
  323.   }
    
  324. 
    
  325.   function mapRemainingChildren(
    
  326.     returnFiber: Fiber,
    
  327.     currentFirstChild: Fiber,
    
  328.   ): Map<string | number, Fiber> {
    
  329.     // Add the remaining children to a temporary map so that we can find them by
    
  330.     // keys quickly. Implicit (null) keys get added to this set with their index
    
  331.     // instead.
    
  332.     const existingChildren: Map<string | number, Fiber> = new Map();
    
  333. 
    
  334.     let existingChild: null | Fiber = currentFirstChild;
    
  335.     while (existingChild !== null) {
    
  336.       if (existingChild.key !== null) {
    
  337.         existingChildren.set(existingChild.key, existingChild);
    
  338.       } else {
    
  339.         existingChildren.set(existingChild.index, existingChild);
    
  340.       }
    
  341.       existingChild = existingChild.sibling;
    
  342.     }
    
  343.     return existingChildren;
    
  344.   }
    
  345. 
    
  346.   function useFiber(fiber: Fiber, pendingProps: mixed): Fiber {
    
  347.     // We currently set sibling to null and index to 0 here because it is easy
    
  348.     // to forget to do before returning it. E.g. for the single child case.
    
  349.     const clone = createWorkInProgress(fiber, pendingProps);
    
  350.     clone.index = 0;
    
  351.     clone.sibling = null;
    
  352.     return clone;
    
  353.   }
    
  354. 
    
  355.   function placeChild(
    
  356.     newFiber: Fiber,
    
  357.     lastPlacedIndex: number,
    
  358.     newIndex: number,
    
  359.   ): number {
    
  360.     newFiber.index = newIndex;
    
  361.     if (!shouldTrackSideEffects) {
    
  362.       // During hydration, the useId algorithm needs to know which fibers are
    
  363.       // part of a list of children (arrays, iterators).
    
  364.       newFiber.flags |= Forked;
    
  365.       return lastPlacedIndex;
    
  366.     }
    
  367.     const current = newFiber.alternate;
    
  368.     if (current !== null) {
    
  369.       const oldIndex = current.index;
    
  370.       if (oldIndex < lastPlacedIndex) {
    
  371.         // This is a move.
    
  372.         newFiber.flags |= Placement | PlacementDEV;
    
  373.         return lastPlacedIndex;
    
  374.       } else {
    
  375.         // This item can stay in place.
    
  376.         return oldIndex;
    
  377.       }
    
  378.     } else {
    
  379.       // This is an insertion.
    
  380.       newFiber.flags |= Placement | PlacementDEV;
    
  381.       return lastPlacedIndex;
    
  382.     }
    
  383.   }
    
  384. 
    
  385.   function placeSingleChild(newFiber: Fiber): Fiber {
    
  386.     // This is simpler for the single child case. We only need to do a
    
  387.     // placement for inserting new children.
    
  388.     if (shouldTrackSideEffects && newFiber.alternate === null) {
    
  389.       newFiber.flags |= Placement | PlacementDEV;
    
  390.     }
    
  391.     return newFiber;
    
  392.   }
    
  393. 
    
  394.   function updateTextNode(
    
  395.     returnFiber: Fiber,
    
  396.     current: Fiber | null,
    
  397.     textContent: string,
    
  398.     lanes: Lanes,
    
  399.   ) {
    
  400.     if (current === null || current.tag !== HostText) {
    
  401.       // Insert
    
  402.       const created = createFiberFromText(textContent, returnFiber.mode, lanes);
    
  403.       created.return = returnFiber;
    
  404.       return created;
    
  405.     } else {
    
  406.       // Update
    
  407.       const existing = useFiber(current, textContent);
    
  408.       existing.return = returnFiber;
    
  409.       return existing;
    
  410.     }
    
  411.   }
    
  412. 
    
  413.   function updateElement(
    
  414.     returnFiber: Fiber,
    
  415.     current: Fiber | null,
    
  416.     element: ReactElement,
    
  417.     lanes: Lanes,
    
  418.   ): Fiber {
    
  419.     const elementType = element.type;
    
  420.     if (elementType === REACT_FRAGMENT_TYPE) {
    
  421.       return updateFragment(
    
  422.         returnFiber,
    
  423.         current,
    
  424.         element.props.children,
    
  425.         lanes,
    
  426.         element.key,
    
  427.       );
    
  428.     }
    
  429.     if (current !== null) {
    
  430.       if (
    
  431.         current.elementType === elementType ||
    
  432.         // Keep this check inline so it only runs on the false path:
    
  433.         (__DEV__
    
  434.           ? isCompatibleFamilyForHotReloading(current, element)
    
  435.           : false) ||
    
  436.         // Lazy types should reconcile their resolved type.
    
  437.         // We need to do this after the Hot Reloading check above,
    
  438.         // because hot reloading has different semantics than prod because
    
  439.         // it doesn't resuspend. So we can't let the call below suspend.
    
  440.         (typeof elementType === 'object' &&
    
  441.           elementType !== null &&
    
  442.           elementType.$$typeof === REACT_LAZY_TYPE &&
    
  443.           resolveLazy(elementType) === current.type)
    
  444.       ) {
    
  445.         // Move based on index
    
  446.         const existing = useFiber(current, element.props);
    
  447.         existing.ref = coerceRef(returnFiber, current, element);
    
  448.         existing.return = returnFiber;
    
  449.         if (__DEV__) {
    
  450.           existing._debugSource = element._source;
    
  451.           existing._debugOwner = element._owner;
    
  452.         }
    
  453.         return existing;
    
  454.       }
    
  455.     }
    
  456.     // Insert
    
  457.     const created = createFiberFromElement(element, returnFiber.mode, lanes);
    
  458.     created.ref = coerceRef(returnFiber, current, element);
    
  459.     created.return = returnFiber;
    
  460.     return created;
    
  461.   }
    
  462. 
    
  463.   function updatePortal(
    
  464.     returnFiber: Fiber,
    
  465.     current: Fiber | null,
    
  466.     portal: ReactPortal,
    
  467.     lanes: Lanes,
    
  468.   ): Fiber {
    
  469.     if (
    
  470.       current === null ||
    
  471.       current.tag !== HostPortal ||
    
  472.       current.stateNode.containerInfo !== portal.containerInfo ||
    
  473.       current.stateNode.implementation !== portal.implementation
    
  474.     ) {
    
  475.       // Insert
    
  476.       const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
    
  477.       created.return = returnFiber;
    
  478.       return created;
    
  479.     } else {
    
  480.       // Update
    
  481.       const existing = useFiber(current, portal.children || []);
    
  482.       existing.return = returnFiber;
    
  483.       return existing;
    
  484.     }
    
  485.   }
    
  486. 
    
  487.   function updateFragment(
    
  488.     returnFiber: Fiber,
    
  489.     current: Fiber | null,
    
  490.     fragment: Iterable<React$Node>,
    
  491.     lanes: Lanes,
    
  492.     key: null | string,
    
  493.   ): Fiber {
    
  494.     if (current === null || current.tag !== Fragment) {
    
  495.       // Insert
    
  496.       const created = createFiberFromFragment(
    
  497.         fragment,
    
  498.         returnFiber.mode,
    
  499.         lanes,
    
  500.         key,
    
  501.       );
    
  502.       created.return = returnFiber;
    
  503.       return created;
    
  504.     } else {
    
  505.       // Update
    
  506.       const existing = useFiber(current, fragment);
    
  507.       existing.return = returnFiber;
    
  508.       return existing;
    
  509.     }
    
  510.   }
    
  511. 
    
  512.   function createChild(
    
  513.     returnFiber: Fiber,
    
  514.     newChild: any,
    
  515.     lanes: Lanes,
    
  516.   ): Fiber | null {
    
  517.     if (
    
  518.       (typeof newChild === 'string' && newChild !== '') ||
    
  519.       typeof newChild === 'number'
    
  520.     ) {
    
  521.       // Text nodes don't have keys. If the previous node is implicitly keyed
    
  522.       // we can continue to replace it without aborting even if it is not a text
    
  523.       // node.
    
  524.       const created = createFiberFromText(
    
  525.         '' + newChild,
    
  526.         returnFiber.mode,
    
  527.         lanes,
    
  528.       );
    
  529.       created.return = returnFiber;
    
  530.       return created;
    
  531.     }
    
  532. 
    
  533.     if (typeof newChild === 'object' && newChild !== null) {
    
  534.       switch (newChild.$$typeof) {
    
  535.         case REACT_ELEMENT_TYPE: {
    
  536.           const created = createFiberFromElement(
    
  537.             newChild,
    
  538.             returnFiber.mode,
    
  539.             lanes,
    
  540.           );
    
  541.           created.ref = coerceRef(returnFiber, null, newChild);
    
  542.           created.return = returnFiber;
    
  543.           return created;
    
  544.         }
    
  545.         case REACT_PORTAL_TYPE: {
    
  546.           const created = createFiberFromPortal(
    
  547.             newChild,
    
  548.             returnFiber.mode,
    
  549.             lanes,
    
  550.           );
    
  551.           created.return = returnFiber;
    
  552.           return created;
    
  553.         }
    
  554.         case REACT_LAZY_TYPE: {
    
  555.           const payload = newChild._payload;
    
  556.           const init = newChild._init;
    
  557.           return createChild(returnFiber, init(payload), lanes);
    
  558.         }
    
  559.       }
    
  560. 
    
  561.       if (isArray(newChild) || getIteratorFn(newChild)) {
    
  562.         const created = createFiberFromFragment(
    
  563.           newChild,
    
  564.           returnFiber.mode,
    
  565.           lanes,
    
  566.           null,
    
  567.         );
    
  568.         created.return = returnFiber;
    
  569.         return created;
    
  570.       }
    
  571. 
    
  572.       // Usable node types
    
  573.       //
    
  574.       // Unwrap the inner value and recursively call this function again.
    
  575.       if (typeof newChild.then === 'function') {
    
  576.         const thenable: Thenable<any> = (newChild: any);
    
  577.         return createChild(returnFiber, unwrapThenable(thenable), lanes);
    
  578.       }
    
  579. 
    
  580.       if (
    
  581.         newChild.$$typeof === REACT_CONTEXT_TYPE ||
    
  582.         newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
    
  583.       ) {
    
  584.         const context: ReactContext<mixed> = (newChild: any);
    
  585.         return createChild(
    
  586.           returnFiber,
    
  587.           readContextDuringReconcilation(returnFiber, context, lanes),
    
  588.           lanes,
    
  589.         );
    
  590.       }
    
  591. 
    
  592.       throwOnInvalidObjectType(returnFiber, newChild);
    
  593.     }
    
  594. 
    
  595.     if (__DEV__) {
    
  596.       if (typeof newChild === 'function') {
    
  597.         warnOnFunctionType(returnFiber);
    
  598.       }
    
  599.     }
    
  600. 
    
  601.     return null;
    
  602.   }
    
  603. 
    
  604.   function updateSlot(
    
  605.     returnFiber: Fiber,
    
  606.     oldFiber: Fiber | null,
    
  607.     newChild: any,
    
  608.     lanes: Lanes,
    
  609.   ): Fiber | null {
    
  610.     // Update the fiber if the keys match, otherwise return null.
    
  611.     const key = oldFiber !== null ? oldFiber.key : null;
    
  612. 
    
  613.     if (
    
  614.       (typeof newChild === 'string' && newChild !== '') ||
    
  615.       typeof newChild === 'number'
    
  616.     ) {
    
  617.       // Text nodes don't have keys. If the previous node is implicitly keyed
    
  618.       // we can continue to replace it without aborting even if it is not a text
    
  619.       // node.
    
  620.       if (key !== null) {
    
  621.         return null;
    
  622.       }
    
  623.       return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);
    
  624.     }
    
  625. 
    
  626.     if (typeof newChild === 'object' && newChild !== null) {
    
  627.       switch (newChild.$$typeof) {
    
  628.         case REACT_ELEMENT_TYPE: {
    
  629.           if (newChild.key === key) {
    
  630.             return updateElement(returnFiber, oldFiber, newChild, lanes);
    
  631.           } else {
    
  632.             return null;
    
  633.           }
    
  634.         }
    
  635.         case REACT_PORTAL_TYPE: {
    
  636.           if (newChild.key === key) {
    
  637.             return updatePortal(returnFiber, oldFiber, newChild, lanes);
    
  638.           } else {
    
  639.             return null;
    
  640.           }
    
  641.         }
    
  642.         case REACT_LAZY_TYPE: {
    
  643.           const payload = newChild._payload;
    
  644.           const init = newChild._init;
    
  645.           return updateSlot(returnFiber, oldFiber, init(payload), lanes);
    
  646.         }
    
  647.       }
    
  648. 
    
  649.       if (isArray(newChild) || getIteratorFn(newChild)) {
    
  650.         if (key !== null) {
    
  651.           return null;
    
  652.         }
    
  653. 
    
  654.         return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
    
  655.       }
    
  656. 
    
  657.       // Usable node types
    
  658.       //
    
  659.       // Unwrap the inner value and recursively call this function again.
    
  660.       if (typeof newChild.then === 'function') {
    
  661.         const thenable: Thenable<any> = (newChild: any);
    
  662.         return updateSlot(
    
  663.           returnFiber,
    
  664.           oldFiber,
    
  665.           unwrapThenable(thenable),
    
  666.           lanes,
    
  667.         );
    
  668.       }
    
  669. 
    
  670.       if (
    
  671.         newChild.$$typeof === REACT_CONTEXT_TYPE ||
    
  672.         newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
    
  673.       ) {
    
  674.         const context: ReactContext<mixed> = (newChild: any);
    
  675.         return updateSlot(
    
  676.           returnFiber,
    
  677.           oldFiber,
    
  678.           readContextDuringReconcilation(returnFiber, context, lanes),
    
  679.           lanes,
    
  680.         );
    
  681.       }
    
  682. 
    
  683.       throwOnInvalidObjectType(returnFiber, newChild);
    
  684.     }
    
  685. 
    
  686.     if (__DEV__) {
    
  687.       if (typeof newChild === 'function') {
    
  688.         warnOnFunctionType(returnFiber);
    
  689.       }
    
  690.     }
    
  691. 
    
  692.     return null;
    
  693.   }
    
  694. 
    
  695.   function updateFromMap(
    
  696.     existingChildren: Map<string | number, Fiber>,
    
  697.     returnFiber: Fiber,
    
  698.     newIdx: number,
    
  699.     newChild: any,
    
  700.     lanes: Lanes,
    
  701.   ): Fiber | null {
    
  702.     if (
    
  703.       (typeof newChild === 'string' && newChild !== '') ||
    
  704.       typeof newChild === 'number'
    
  705.     ) {
    
  706.       // Text nodes don't have keys, so we neither have to check the old nor
    
  707.       // new node for the key. If both are text nodes, they match.
    
  708.       const matchedFiber = existingChildren.get(newIdx) || null;
    
  709.       return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes);
    
  710.     }
    
  711. 
    
  712.     if (typeof newChild === 'object' && newChild !== null) {
    
  713.       switch (newChild.$$typeof) {
    
  714.         case REACT_ELEMENT_TYPE: {
    
  715.           const matchedFiber =
    
  716.             existingChildren.get(
    
  717.               newChild.key === null ? newIdx : newChild.key,
    
  718.             ) || null;
    
  719.           return updateElement(returnFiber, matchedFiber, newChild, lanes);
    
  720.         }
    
  721.         case REACT_PORTAL_TYPE: {
    
  722.           const matchedFiber =
    
  723.             existingChildren.get(
    
  724.               newChild.key === null ? newIdx : newChild.key,
    
  725.             ) || null;
    
  726.           return updatePortal(returnFiber, matchedFiber, newChild, lanes);
    
  727.         }
    
  728.         case REACT_LAZY_TYPE:
    
  729.           const payload = newChild._payload;
    
  730.           const init = newChild._init;
    
  731.           return updateFromMap(
    
  732.             existingChildren,
    
  733.             returnFiber,
    
  734.             newIdx,
    
  735.             init(payload),
    
  736.             lanes,
    
  737.           );
    
  738.       }
    
  739. 
    
  740.       if (isArray(newChild) || getIteratorFn(newChild)) {
    
  741.         const matchedFiber = existingChildren.get(newIdx) || null;
    
  742.         return updateFragment(returnFiber, matchedFiber, newChild, lanes, null);
    
  743.       }
    
  744. 
    
  745.       // Usable node types
    
  746.       //
    
  747.       // Unwrap the inner value and recursively call this function again.
    
  748.       if (typeof newChild.then === 'function') {
    
  749.         const thenable: Thenable<any> = (newChild: any);
    
  750.         return updateFromMap(
    
  751.           existingChildren,
    
  752.           returnFiber,
    
  753.           newIdx,
    
  754.           unwrapThenable(thenable),
    
  755.           lanes,
    
  756.         );
    
  757.       }
    
  758. 
    
  759.       if (
    
  760.         newChild.$$typeof === REACT_CONTEXT_TYPE ||
    
  761.         newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
    
  762.       ) {
    
  763.         const context: ReactContext<mixed> = (newChild: any);
    
  764.         return updateFromMap(
    
  765.           existingChildren,
    
  766.           returnFiber,
    
  767.           newIdx,
    
  768.           readContextDuringReconcilation(returnFiber, context, lanes),
    
  769.           lanes,
    
  770.         );
    
  771.       }
    
  772. 
    
  773.       throwOnInvalidObjectType(returnFiber, newChild);
    
  774.     }
    
  775. 
    
  776.     if (__DEV__) {
    
  777.       if (typeof newChild === 'function') {
    
  778.         warnOnFunctionType(returnFiber);
    
  779.       }
    
  780.     }
    
  781. 
    
  782.     return null;
    
  783.   }
    
  784. 
    
  785.   /**
    
  786.    * Warns if there is a duplicate or missing key
    
  787.    */
    
  788.   function warnOnInvalidKey(
    
  789.     child: mixed,
    
  790.     knownKeys: Set<string> | null,
    
  791.     returnFiber: Fiber,
    
  792.   ): Set<string> | null {
    
  793.     if (__DEV__) {
    
  794.       if (typeof child !== 'object' || child === null) {
    
  795.         return knownKeys;
    
  796.       }
    
  797.       switch (child.$$typeof) {
    
  798.         case REACT_ELEMENT_TYPE:
    
  799.         case REACT_PORTAL_TYPE:
    
  800.           warnForMissingKey(child, returnFiber);
    
  801.           const key = child.key;
    
  802.           if (typeof key !== 'string') {
    
  803.             break;
    
  804.           }
    
  805.           if (knownKeys === null) {
    
  806.             knownKeys = new Set();
    
  807.             knownKeys.add(key);
    
  808.             break;
    
  809.           }
    
  810.           if (!knownKeys.has(key)) {
    
  811.             knownKeys.add(key);
    
  812.             break;
    
  813.           }
    
  814.           console.error(
    
  815.             'Encountered two children with the same key, `%s`. ' +
    
  816.               'Keys should be unique so that components maintain their identity ' +
    
  817.               'across updates. Non-unique keys may cause children to be ' +
    
  818.               'duplicated and/or omitted — the behavior is unsupported and ' +
    
  819.               'could change in a future version.',
    
  820.             key,
    
  821.           );
    
  822.           break;
    
  823.         case REACT_LAZY_TYPE:
    
  824.           const payload = child._payload;
    
  825.           const init = (child._init: any);
    
  826.           warnOnInvalidKey(init(payload), knownKeys, returnFiber);
    
  827.           break;
    
  828.         default:
    
  829.           break;
    
  830.       }
    
  831.     }
    
  832.     return knownKeys;
    
  833.   }
    
  834. 
    
  835.   function reconcileChildrenArray(
    
  836.     returnFiber: Fiber,
    
  837.     currentFirstChild: Fiber | null,
    
  838.     newChildren: Array<any>,
    
  839.     lanes: Lanes,
    
  840.   ): Fiber | null {
    
  841.     // This algorithm can't optimize by searching from both ends since we
    
  842.     // don't have backpointers on fibers. I'm trying to see how far we can get
    
  843.     // with that model. If it ends up not being worth the tradeoffs, we can
    
  844.     // add it later.
    
  845. 
    
  846.     // Even with a two ended optimization, we'd want to optimize for the case
    
  847.     // where there are few changes and brute force the comparison instead of
    
  848.     // going for the Map. It'd like to explore hitting that path first in
    
  849.     // forward-only mode and only go for the Map once we notice that we need
    
  850.     // lots of look ahead. This doesn't handle reversal as well as two ended
    
  851.     // search but that's unusual. Besides, for the two ended optimization to
    
  852.     // work on Iterables, we'd need to copy the whole set.
    
  853. 
    
  854.     // In this first iteration, we'll just live with hitting the bad case
    
  855.     // (adding everything to a Map) in for every insert/move.
    
  856. 
    
  857.     // If you change this code, also update reconcileChildrenIterator() which
    
  858.     // uses the same algorithm.
    
  859. 
    
  860.     if (__DEV__) {
    
  861.       // First, validate keys.
    
  862.       let knownKeys: Set<string> | null = null;
    
  863.       for (let i = 0; i < newChildren.length; i++) {
    
  864.         const child = newChildren[i];
    
  865.         knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
    
  866.       }
    
  867.     }
    
  868. 
    
  869.     let resultingFirstChild: Fiber | null = null;
    
  870.     let previousNewFiber: Fiber | null = null;
    
  871. 
    
  872.     let oldFiber = currentFirstChild;
    
  873.     let lastPlacedIndex = 0;
    
  874.     let newIdx = 0;
    
  875.     let nextOldFiber = null;
    
  876.     for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
    
  877.       if (oldFiber.index > newIdx) {
    
  878.         nextOldFiber = oldFiber;
    
  879.         oldFiber = null;
    
  880.       } else {
    
  881.         nextOldFiber = oldFiber.sibling;
    
  882.       }
    
  883.       const newFiber = updateSlot(
    
  884.         returnFiber,
    
  885.         oldFiber,
    
  886.         newChildren[newIdx],
    
  887.         lanes,
    
  888.       );
    
  889.       if (newFiber === null) {
    
  890.         // TODO: This breaks on empty slots like null children. That's
    
  891.         // unfortunate because it triggers the slow path all the time. We need
    
  892.         // a better way to communicate whether this was a miss or null,
    
  893.         // boolean, undefined, etc.
    
  894.         if (oldFiber === null) {
    
  895.           oldFiber = nextOldFiber;
    
  896.         }
    
  897.         break;
    
  898.       }
    
  899.       if (shouldTrackSideEffects) {
    
  900.         if (oldFiber && newFiber.alternate === null) {
    
  901.           // We matched the slot, but we didn't reuse the existing fiber, so we
    
  902.           // need to delete the existing child.
    
  903.           deleteChild(returnFiber, oldFiber);
    
  904.         }
    
  905.       }
    
  906.       lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  907.       if (previousNewFiber === null) {
    
  908.         // TODO: Move out of the loop. This only happens for the first run.
    
  909.         resultingFirstChild = newFiber;
    
  910.       } else {
    
  911.         // TODO: Defer siblings if we're not at the right index for this slot.
    
  912.         // I.e. if we had null values before, then we want to defer this
    
  913.         // for each null value. However, we also don't want to call updateSlot
    
  914.         // with the previous one.
    
  915.         previousNewFiber.sibling = newFiber;
    
  916.       }
    
  917.       previousNewFiber = newFiber;
    
  918.       oldFiber = nextOldFiber;
    
  919.     }
    
  920. 
    
  921.     if (newIdx === newChildren.length) {
    
  922.       // We've reached the end of the new children. We can delete the rest.
    
  923.       deleteRemainingChildren(returnFiber, oldFiber);
    
  924.       if (getIsHydrating()) {
    
  925.         const numberOfForks = newIdx;
    
  926.         pushTreeFork(returnFiber, numberOfForks);
    
  927.       }
    
  928.       return resultingFirstChild;
    
  929.     }
    
  930. 
    
  931.     if (oldFiber === null) {
    
  932.       // If we don't have any more existing children we can choose a fast path
    
  933.       // since the rest will all be insertions.
    
  934.       for (; newIdx < newChildren.length; newIdx++) {
    
  935.         const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
    
  936.         if (newFiber === null) {
    
  937.           continue;
    
  938.         }
    
  939.         lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  940.         if (previousNewFiber === null) {
    
  941.           // TODO: Move out of the loop. This only happens for the first run.
    
  942.           resultingFirstChild = newFiber;
    
  943.         } else {
    
  944.           previousNewFiber.sibling = newFiber;
    
  945.         }
    
  946.         previousNewFiber = newFiber;
    
  947.       }
    
  948.       if (getIsHydrating()) {
    
  949.         const numberOfForks = newIdx;
    
  950.         pushTreeFork(returnFiber, numberOfForks);
    
  951.       }
    
  952.       return resultingFirstChild;
    
  953.     }
    
  954. 
    
  955.     // Add all children to a key map for quick lookups.
    
  956.     const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
    
  957. 
    
  958.     // Keep scanning and use the map to restore deleted items as moves.
    
  959.     for (; newIdx < newChildren.length; newIdx++) {
    
  960.       const newFiber = updateFromMap(
    
  961.         existingChildren,
    
  962.         returnFiber,
    
  963.         newIdx,
    
  964.         newChildren[newIdx],
    
  965.         lanes,
    
  966.       );
    
  967.       if (newFiber !== null) {
    
  968.         if (shouldTrackSideEffects) {
    
  969.           if (newFiber.alternate !== null) {
    
  970.             // The new fiber is a work in progress, but if there exists a
    
  971.             // current, that means that we reused the fiber. We need to delete
    
  972.             // it from the child list so that we don't add it to the deletion
    
  973.             // list.
    
  974.             existingChildren.delete(
    
  975.               newFiber.key === null ? newIdx : newFiber.key,
    
  976.             );
    
  977.           }
    
  978.         }
    
  979.         lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  980.         if (previousNewFiber === null) {
    
  981.           resultingFirstChild = newFiber;
    
  982.         } else {
    
  983.           previousNewFiber.sibling = newFiber;
    
  984.         }
    
  985.         previousNewFiber = newFiber;
    
  986.       }
    
  987.     }
    
  988. 
    
  989.     if (shouldTrackSideEffects) {
    
  990.       // Any existing children that weren't consumed above were deleted. We need
    
  991.       // to add them to the deletion list.
    
  992.       existingChildren.forEach(child => deleteChild(returnFiber, child));
    
  993.     }
    
  994. 
    
  995.     if (getIsHydrating()) {
    
  996.       const numberOfForks = newIdx;
    
  997.       pushTreeFork(returnFiber, numberOfForks);
    
  998.     }
    
  999.     return resultingFirstChild;
    
  1000.   }
    
  1001. 
    
  1002.   function reconcileChildrenIterator(
    
  1003.     returnFiber: Fiber,
    
  1004.     currentFirstChild: Fiber | null,
    
  1005.     newChildrenIterable: Iterable<mixed>,
    
  1006.     lanes: Lanes,
    
  1007.   ): Fiber | null {
    
  1008.     // This is the same implementation as reconcileChildrenArray(),
    
  1009.     // but using the iterator instead.
    
  1010. 
    
  1011.     const iteratorFn = getIteratorFn(newChildrenIterable);
    
  1012. 
    
  1013.     if (typeof iteratorFn !== 'function') {
    
  1014.       throw new Error(
    
  1015.         'An object is not an iterable. This error is likely caused by a bug in ' +
    
  1016.           'React. Please file an issue.',
    
  1017.       );
    
  1018.     }
    
  1019. 
    
  1020.     if (__DEV__) {
    
  1021.       // We don't support rendering Generators because it's a mutation.
    
  1022.       // See https://github.com/facebook/react/issues/12995
    
  1023.       if (
    
  1024.         typeof Symbol === 'function' &&
    
  1025.         // $FlowFixMe[prop-missing] Flow doesn't know about toStringTag
    
  1026.         newChildrenIterable[Symbol.toStringTag] === 'Generator'
    
  1027.       ) {
    
  1028.         if (!didWarnAboutGenerators) {
    
  1029.           console.error(
    
  1030.             'Using Generators as children is unsupported and will likely yield ' +
    
  1031.               'unexpected results because enumerating a generator mutates it. ' +
    
  1032.               'You may convert it to an array with `Array.from()` or the ' +
    
  1033.               '`[...spread]` operator before rendering. Keep in mind ' +
    
  1034.               'you might need to polyfill these features for older browsers.',
    
  1035.           );
    
  1036.         }
    
  1037.         didWarnAboutGenerators = true;
    
  1038.       }
    
  1039. 
    
  1040.       // Warn about using Maps as children
    
  1041.       if ((newChildrenIterable: any).entries === iteratorFn) {
    
  1042.         if (!didWarnAboutMaps) {
    
  1043.           console.error(
    
  1044.             'Using Maps as children is not supported. ' +
    
  1045.               'Use an array of keyed ReactElements instead.',
    
  1046.           );
    
  1047.         }
    
  1048.         didWarnAboutMaps = true;
    
  1049.       }
    
  1050. 
    
  1051.       // First, validate keys.
    
  1052.       // We'll get a different iterator later for the main pass.
    
  1053.       const newChildren = iteratorFn.call(newChildrenIterable);
    
  1054.       if (newChildren) {
    
  1055.         let knownKeys: Set<string> | null = null;
    
  1056.         let step = newChildren.next();
    
  1057.         for (; !step.done; step = newChildren.next()) {
    
  1058.           const child = step.value;
    
  1059.           knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
    
  1060.         }
    
  1061.       }
    
  1062.     }
    
  1063. 
    
  1064.     const newChildren = iteratorFn.call(newChildrenIterable);
    
  1065. 
    
  1066.     if (newChildren == null) {
    
  1067.       throw new Error('An iterable object provided no iterator.');
    
  1068.     }
    
  1069. 
    
  1070.     let resultingFirstChild: Fiber | null = null;
    
  1071.     let previousNewFiber: Fiber | null = null;
    
  1072. 
    
  1073.     let oldFiber = currentFirstChild;
    
  1074.     let lastPlacedIndex = 0;
    
  1075.     let newIdx = 0;
    
  1076.     let nextOldFiber = null;
    
  1077. 
    
  1078.     let step = newChildren.next();
    
  1079.     for (
    
  1080.       ;
    
  1081.       oldFiber !== null && !step.done;
    
  1082.       newIdx++, step = newChildren.next()
    
  1083.     ) {
    
  1084.       if (oldFiber.index > newIdx) {
    
  1085.         nextOldFiber = oldFiber;
    
  1086.         oldFiber = null;
    
  1087.       } else {
    
  1088.         nextOldFiber = oldFiber.sibling;
    
  1089.       }
    
  1090.       const newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
    
  1091.       if (newFiber === null) {
    
  1092.         // TODO: This breaks on empty slots like null children. That's
    
  1093.         // unfortunate because it triggers the slow path all the time. We need
    
  1094.         // a better way to communicate whether this was a miss or null,
    
  1095.         // boolean, undefined, etc.
    
  1096.         if (oldFiber === null) {
    
  1097.           oldFiber = nextOldFiber;
    
  1098.         }
    
  1099.         break;
    
  1100.       }
    
  1101.       if (shouldTrackSideEffects) {
    
  1102.         if (oldFiber && newFiber.alternate === null) {
    
  1103.           // We matched the slot, but we didn't reuse the existing fiber, so we
    
  1104.           // need to delete the existing child.
    
  1105.           deleteChild(returnFiber, oldFiber);
    
  1106.         }
    
  1107.       }
    
  1108.       lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  1109.       if (previousNewFiber === null) {
    
  1110.         // TODO: Move out of the loop. This only happens for the first run.
    
  1111.         resultingFirstChild = newFiber;
    
  1112.       } else {
    
  1113.         // TODO: Defer siblings if we're not at the right index for this slot.
    
  1114.         // I.e. if we had null values before, then we want to defer this
    
  1115.         // for each null value. However, we also don't want to call updateSlot
    
  1116.         // with the previous one.
    
  1117.         previousNewFiber.sibling = newFiber;
    
  1118.       }
    
  1119.       previousNewFiber = newFiber;
    
  1120.       oldFiber = nextOldFiber;
    
  1121.     }
    
  1122. 
    
  1123.     if (step.done) {
    
  1124.       // We've reached the end of the new children. We can delete the rest.
    
  1125.       deleteRemainingChildren(returnFiber, oldFiber);
    
  1126.       if (getIsHydrating()) {
    
  1127.         const numberOfForks = newIdx;
    
  1128.         pushTreeFork(returnFiber, numberOfForks);
    
  1129.       }
    
  1130.       return resultingFirstChild;
    
  1131.     }
    
  1132. 
    
  1133.     if (oldFiber === null) {
    
  1134.       // If we don't have any more existing children we can choose a fast path
    
  1135.       // since the rest will all be insertions.
    
  1136.       for (; !step.done; newIdx++, step = newChildren.next()) {
    
  1137.         const newFiber = createChild(returnFiber, step.value, lanes);
    
  1138.         if (newFiber === null) {
    
  1139.           continue;
    
  1140.         }
    
  1141.         lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  1142.         if (previousNewFiber === null) {
    
  1143.           // TODO: Move out of the loop. This only happens for the first run.
    
  1144.           resultingFirstChild = newFiber;
    
  1145.         } else {
    
  1146.           previousNewFiber.sibling = newFiber;
    
  1147.         }
    
  1148.         previousNewFiber = newFiber;
    
  1149.       }
    
  1150.       if (getIsHydrating()) {
    
  1151.         const numberOfForks = newIdx;
    
  1152.         pushTreeFork(returnFiber, numberOfForks);
    
  1153.       }
    
  1154.       return resultingFirstChild;
    
  1155.     }
    
  1156. 
    
  1157.     // Add all children to a key map for quick lookups.
    
  1158.     const existingChildren = mapRemainingChildren(returnFiber, oldFiber);
    
  1159. 
    
  1160.     // Keep scanning and use the map to restore deleted items as moves.
    
  1161.     for (; !step.done; newIdx++, step = newChildren.next()) {
    
  1162.       const newFiber = updateFromMap(
    
  1163.         existingChildren,
    
  1164.         returnFiber,
    
  1165.         newIdx,
    
  1166.         step.value,
    
  1167.         lanes,
    
  1168.       );
    
  1169.       if (newFiber !== null) {
    
  1170.         if (shouldTrackSideEffects) {
    
  1171.           if (newFiber.alternate !== null) {
    
  1172.             // The new fiber is a work in progress, but if there exists a
    
  1173.             // current, that means that we reused the fiber. We need to delete
    
  1174.             // it from the child list so that we don't add it to the deletion
    
  1175.             // list.
    
  1176.             existingChildren.delete(
    
  1177.               newFiber.key === null ? newIdx : newFiber.key,
    
  1178.             );
    
  1179.           }
    
  1180.         }
    
  1181.         lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    
  1182.         if (previousNewFiber === null) {
    
  1183.           resultingFirstChild = newFiber;
    
  1184.         } else {
    
  1185.           previousNewFiber.sibling = newFiber;
    
  1186.         }
    
  1187.         previousNewFiber = newFiber;
    
  1188.       }
    
  1189.     }
    
  1190. 
    
  1191.     if (shouldTrackSideEffects) {
    
  1192.       // Any existing children that weren't consumed above were deleted. We need
    
  1193.       // to add them to the deletion list.
    
  1194.       existingChildren.forEach(child => deleteChild(returnFiber, child));
    
  1195.     }
    
  1196. 
    
  1197.     if (getIsHydrating()) {
    
  1198.       const numberOfForks = newIdx;
    
  1199.       pushTreeFork(returnFiber, numberOfForks);
    
  1200.     }
    
  1201.     return resultingFirstChild;
    
  1202.   }
    
  1203. 
    
  1204.   function reconcileSingleTextNode(
    
  1205.     returnFiber: Fiber,
    
  1206.     currentFirstChild: Fiber | null,
    
  1207.     textContent: string,
    
  1208.     lanes: Lanes,
    
  1209.   ): Fiber {
    
  1210.     // There's no need to check for keys on text nodes since we don't have a
    
  1211.     // way to define them.
    
  1212.     if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
    
  1213.       // We already have an existing node so let's just update it and delete
    
  1214.       // the rest.
    
  1215.       deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
    
  1216.       const existing = useFiber(currentFirstChild, textContent);
    
  1217.       existing.return = returnFiber;
    
  1218.       return existing;
    
  1219.     }
    
  1220.     // The existing first child is not a text node so we need to create one
    
  1221.     // and delete the existing ones.
    
  1222.     deleteRemainingChildren(returnFiber, currentFirstChild);
    
  1223.     const created = createFiberFromText(textContent, returnFiber.mode, lanes);
    
  1224.     created.return = returnFiber;
    
  1225.     return created;
    
  1226.   }
    
  1227. 
    
  1228.   function reconcileSingleElement(
    
  1229.     returnFiber: Fiber,
    
  1230.     currentFirstChild: Fiber | null,
    
  1231.     element: ReactElement,
    
  1232.     lanes: Lanes,
    
  1233.   ): Fiber {
    
  1234.     const key = element.key;
    
  1235.     let child = currentFirstChild;
    
  1236.     while (child !== null) {
    
  1237.       // TODO: If key === null and child.key === null, then this only applies to
    
  1238.       // the first item in the list.
    
  1239.       if (child.key === key) {
    
  1240.         const elementType = element.type;
    
  1241.         if (elementType === REACT_FRAGMENT_TYPE) {
    
  1242.           if (child.tag === Fragment) {
    
  1243.             deleteRemainingChildren(returnFiber, child.sibling);
    
  1244.             const existing = useFiber(child, element.props.children);
    
  1245.             existing.return = returnFiber;
    
  1246.             if (__DEV__) {
    
  1247.               existing._debugSource = element._source;
    
  1248.               existing._debugOwner = element._owner;
    
  1249.             }
    
  1250.             return existing;
    
  1251.           }
    
  1252.         } else {
    
  1253.           if (
    
  1254.             child.elementType === elementType ||
    
  1255.             // Keep this check inline so it only runs on the false path:
    
  1256.             (__DEV__
    
  1257.               ? isCompatibleFamilyForHotReloading(child, element)
    
  1258.               : false) ||
    
  1259.             // Lazy types should reconcile their resolved type.
    
  1260.             // We need to do this after the Hot Reloading check above,
    
  1261.             // because hot reloading has different semantics than prod because
    
  1262.             // it doesn't resuspend. So we can't let the call below suspend.
    
  1263.             (typeof elementType === 'object' &&
    
  1264.               elementType !== null &&
    
  1265.               elementType.$$typeof === REACT_LAZY_TYPE &&
    
  1266.               resolveLazy(elementType) === child.type)
    
  1267.           ) {
    
  1268.             deleteRemainingChildren(returnFiber, child.sibling);
    
  1269.             const existing = useFiber(child, element.props);
    
  1270.             existing.ref = coerceRef(returnFiber, child, element);
    
  1271.             existing.return = returnFiber;
    
  1272.             if (__DEV__) {
    
  1273.               existing._debugSource = element._source;
    
  1274.               existing._debugOwner = element._owner;
    
  1275.             }
    
  1276.             return existing;
    
  1277.           }
    
  1278.         }
    
  1279.         // Didn't match.
    
  1280.         deleteRemainingChildren(returnFiber, child);
    
  1281.         break;
    
  1282.       } else {
    
  1283.         deleteChild(returnFiber, child);
    
  1284.       }
    
  1285.       child = child.sibling;
    
  1286.     }
    
  1287. 
    
  1288.     if (element.type === REACT_FRAGMENT_TYPE) {
    
  1289.       const created = createFiberFromFragment(
    
  1290.         element.props.children,
    
  1291.         returnFiber.mode,
    
  1292.         lanes,
    
  1293.         element.key,
    
  1294.       );
    
  1295.       created.return = returnFiber;
    
  1296.       return created;
    
  1297.     } else {
    
  1298.       const created = createFiberFromElement(element, returnFiber.mode, lanes);
    
  1299.       created.ref = coerceRef(returnFiber, currentFirstChild, element);
    
  1300.       created.return = returnFiber;
    
  1301.       return created;
    
  1302.     }
    
  1303.   }
    
  1304. 
    
  1305.   function reconcileSinglePortal(
    
  1306.     returnFiber: Fiber,
    
  1307.     currentFirstChild: Fiber | null,
    
  1308.     portal: ReactPortal,
    
  1309.     lanes: Lanes,
    
  1310.   ): Fiber {
    
  1311.     const key = portal.key;
    
  1312.     let child = currentFirstChild;
    
  1313.     while (child !== null) {
    
  1314.       // TODO: If key === null and child.key === null, then this only applies to
    
  1315.       // the first item in the list.
    
  1316.       if (child.key === key) {
    
  1317.         if (
    
  1318.           child.tag === HostPortal &&
    
  1319.           child.stateNode.containerInfo === portal.containerInfo &&
    
  1320.           child.stateNode.implementation === portal.implementation
    
  1321.         ) {
    
  1322.           deleteRemainingChildren(returnFiber, child.sibling);
    
  1323.           const existing = useFiber(child, portal.children || []);
    
  1324.           existing.return = returnFiber;
    
  1325.           return existing;
    
  1326.         } else {
    
  1327.           deleteRemainingChildren(returnFiber, child);
    
  1328.           break;
    
  1329.         }
    
  1330.       } else {
    
  1331.         deleteChild(returnFiber, child);
    
  1332.       }
    
  1333.       child = child.sibling;
    
  1334.     }
    
  1335. 
    
  1336.     const created = createFiberFromPortal(portal, returnFiber.mode, lanes);
    
  1337.     created.return = returnFiber;
    
  1338.     return created;
    
  1339.   }
    
  1340. 
    
  1341.   // This API will tag the children with the side-effect of the reconciliation
    
  1342.   // itself. They will be added to the side-effect list as we pass through the
    
  1343.   // children and the parent.
    
  1344.   function reconcileChildFibersImpl(
    
  1345.     returnFiber: Fiber,
    
  1346.     currentFirstChild: Fiber | null,
    
  1347.     newChild: any,
    
  1348.     lanes: Lanes,
    
  1349.   ): Fiber | null {
    
  1350.     // This function is not recursive.
    
  1351.     // If the top level item is an array, we treat it as a set of children,
    
  1352.     // not as a fragment. Nested arrays on the other hand will be treated as
    
  1353.     // fragment nodes. Recursion happens at the normal flow.
    
  1354. 
    
  1355.     // Handle top level unkeyed fragments as if they were arrays.
    
  1356.     // This leads to an ambiguity between <>{[...]}</> and <>...</>.
    
  1357.     // We treat the ambiguous cases above the same.
    
  1358.     // TODO: Let's use recursion like we do for Usable nodes?
    
  1359.     const isUnkeyedTopLevelFragment =
    
  1360.       typeof newChild === 'object' &&
    
  1361.       newChild !== null &&
    
  1362.       newChild.type === REACT_FRAGMENT_TYPE &&
    
  1363.       newChild.key === null;
    
  1364.     if (isUnkeyedTopLevelFragment) {
    
  1365.       newChild = newChild.props.children;
    
  1366.     }
    
  1367. 
    
  1368.     // Handle object types
    
  1369.     if (typeof newChild === 'object' && newChild !== null) {
    
  1370.       switch (newChild.$$typeof) {
    
  1371.         case REACT_ELEMENT_TYPE:
    
  1372.           return placeSingleChild(
    
  1373.             reconcileSingleElement(
    
  1374.               returnFiber,
    
  1375.               currentFirstChild,
    
  1376.               newChild,
    
  1377.               lanes,
    
  1378.             ),
    
  1379.           );
    
  1380.         case REACT_PORTAL_TYPE:
    
  1381.           return placeSingleChild(
    
  1382.             reconcileSinglePortal(
    
  1383.               returnFiber,
    
  1384.               currentFirstChild,
    
  1385.               newChild,
    
  1386.               lanes,
    
  1387.             ),
    
  1388.           );
    
  1389.         case REACT_LAZY_TYPE:
    
  1390.           const payload = newChild._payload;
    
  1391.           const init = newChild._init;
    
  1392.           // TODO: This function is supposed to be non-recursive.
    
  1393.           return reconcileChildFibers(
    
  1394.             returnFiber,
    
  1395.             currentFirstChild,
    
  1396.             init(payload),
    
  1397.             lanes,
    
  1398.           );
    
  1399.       }
    
  1400. 
    
  1401.       if (isArray(newChild)) {
    
  1402.         return reconcileChildrenArray(
    
  1403.           returnFiber,
    
  1404.           currentFirstChild,
    
  1405.           newChild,
    
  1406.           lanes,
    
  1407.         );
    
  1408.       }
    
  1409. 
    
  1410.       if (getIteratorFn(newChild)) {
    
  1411.         return reconcileChildrenIterator(
    
  1412.           returnFiber,
    
  1413.           currentFirstChild,
    
  1414.           newChild,
    
  1415.           lanes,
    
  1416.         );
    
  1417.       }
    
  1418. 
    
  1419.       // Usables are a valid React node type. When React encounters a Usable in
    
  1420.       // a child position, it unwraps it using the same algorithm as `use`. For
    
  1421.       // example, for promises, React will throw an exception to unwind the
    
  1422.       // stack, then replay the component once the promise resolves.
    
  1423.       //
    
  1424.       // A difference from `use` is that React will keep unwrapping the value
    
  1425.       // until it reaches a non-Usable type.
    
  1426.       //
    
  1427.       // e.g. Usable<Usable<Usable<T>>> should resolve to T
    
  1428.       //
    
  1429.       // The structure is a bit unfortunate. Ideally, we shouldn't need to
    
  1430.       // replay the entire begin phase of the parent fiber in order to reconcile
    
  1431.       // the children again. This would require a somewhat significant refactor,
    
  1432.       // because reconcilation happens deep within the begin phase, and
    
  1433.       // depending on the type of work, not always at the end. We should
    
  1434.       // consider as an future improvement.
    
  1435.       if (typeof newChild.then === 'function') {
    
  1436.         const thenable: Thenable<any> = (newChild: any);
    
  1437.         return reconcileChildFibersImpl(
    
  1438.           returnFiber,
    
  1439.           currentFirstChild,
    
  1440.           unwrapThenable(thenable),
    
  1441.           lanes,
    
  1442.         );
    
  1443.       }
    
  1444. 
    
  1445.       if (
    
  1446.         newChild.$$typeof === REACT_CONTEXT_TYPE ||
    
  1447.         newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
    
  1448.       ) {
    
  1449.         const context: ReactContext<mixed> = (newChild: any);
    
  1450.         return reconcileChildFibersImpl(
    
  1451.           returnFiber,
    
  1452.           currentFirstChild,
    
  1453.           readContextDuringReconcilation(returnFiber, context, lanes),
    
  1454.           lanes,
    
  1455.         );
    
  1456.       }
    
  1457. 
    
  1458.       throwOnInvalidObjectType(returnFiber, newChild);
    
  1459.     }
    
  1460. 
    
  1461.     if (
    
  1462.       (typeof newChild === 'string' && newChild !== '') ||
    
  1463.       typeof newChild === 'number'
    
  1464.     ) {
    
  1465.       return placeSingleChild(
    
  1466.         reconcileSingleTextNode(
    
  1467.           returnFiber,
    
  1468.           currentFirstChild,
    
  1469.           '' + newChild,
    
  1470.           lanes,
    
  1471.         ),
    
  1472.       );
    
  1473.     }
    
  1474. 
    
  1475.     if (__DEV__) {
    
  1476.       if (typeof newChild === 'function') {
    
  1477.         warnOnFunctionType(returnFiber);
    
  1478.       }
    
  1479.     }
    
  1480. 
    
  1481.     // Remaining cases are all treated as empty.
    
  1482.     return deleteRemainingChildren(returnFiber, currentFirstChild);
    
  1483.   }
    
  1484. 
    
  1485.   function reconcileChildFibers(
    
  1486.     returnFiber: Fiber,
    
  1487.     currentFirstChild: Fiber | null,
    
  1488.     newChild: any,
    
  1489.     lanes: Lanes,
    
  1490.   ): Fiber | null {
    
  1491.     // This indirection only exists so we can reset `thenableState` at the end.
    
  1492.     // It should get inlined by Closure.
    
  1493.     thenableIndexCounter = 0;
    
  1494.     const firstChildFiber = reconcileChildFibersImpl(
    
  1495.       returnFiber,
    
  1496.       currentFirstChild,
    
  1497.       newChild,
    
  1498.       lanes,
    
  1499.     );
    
  1500.     thenableState = null;
    
  1501.     // Don't bother to reset `thenableIndexCounter` to 0 because it always gets
    
  1502.     // set at the beginning.
    
  1503.     return firstChildFiber;
    
  1504.   }
    
  1505. 
    
  1506.   return reconcileChildFibers;
    
  1507. }
    
  1508. 
    
  1509. export const reconcileChildFibers: ChildReconciler =
    
  1510.   createChildReconciler(true);
    
  1511. export const mountChildFibers: ChildReconciler = createChildReconciler(false);
    
  1512. 
    
  1513. export function resetChildReconcilerOnUnwind(): void {
    
  1514.   // On unwind, clear any pending thenables that were used.
    
  1515.   thenableState = null;
    
  1516.   thenableIndexCounter = 0;
    
  1517. }
    
  1518. 
    
  1519. export function cloneChildFibers(
    
  1520.   current: Fiber | null,
    
  1521.   workInProgress: Fiber,
    
  1522. ): void {
    
  1523.   if (current !== null && workInProgress.child !== current.child) {
    
  1524.     throw new Error('Resuming work not yet implemented.');
    
  1525.   }
    
  1526. 
    
  1527.   if (workInProgress.child === null) {
    
  1528.     return;
    
  1529.   }
    
  1530. 
    
  1531.   let currentChild = workInProgress.child;
    
  1532.   let newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
    
  1533.   workInProgress.child = newChild;
    
  1534. 
    
  1535.   newChild.return = workInProgress;
    
  1536.   while (currentChild.sibling !== null) {
    
  1537.     currentChild = currentChild.sibling;
    
  1538.     newChild = newChild.sibling = createWorkInProgress(
    
  1539.       currentChild,
    
  1540.       currentChild.pendingProps,
    
  1541.     );
    
  1542.     newChild.return = workInProgress;
    
  1543.   }
    
  1544.   newChild.sibling = null;
    
  1545. }
    
  1546. 
    
  1547. // Reset a workInProgress child set to prepare it for a second pass.
    
  1548. export function resetChildFibers(workInProgress: Fiber, lanes: Lanes): void {
    
  1549.   let child = workInProgress.child;
    
  1550.   while (child !== null) {
    
  1551.     resetWorkInProgress(child, lanes);
    
  1552.     child = child.sibling;
    
  1553.   }
    
  1554. }