packages/react-reconciler/src/ReactChildFiber.js JAVASCRIPT 2,260 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,260.
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {ReactElement} from 'shared/ReactElementType';11import type {12  ReactPortal,13  Thenable,14  ReactContext,15  ReactDebugInfo,16  ReactComponentInfo,17  SuspenseListRevealOrder,18  ReactKey,19  ReactOptimisticKey,20} from 'shared/ReactTypes';21import type {Fiber} from './ReactInternalTypes';22import type {Lanes} from './ReactFiberLane';23import type {ThenableState} from './ReactFiberThenable';2425import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';26import {27  Placement,28  ChildDeletion,29  Forked,30  PlacementDEV,31} from './ReactFiberFlags';32import {NoMode, ConcurrentMode} from './ReactTypeOfMode';33import {34  getIteratorFn,35  ASYNC_ITERATOR,36  REACT_ELEMENT_TYPE,37  REACT_FRAGMENT_TYPE,38  REACT_PORTAL_TYPE,39  REACT_LAZY_TYPE,40  REACT_CONTEXT_TYPE,41  REACT_LEGACY_ELEMENT_TYPE,42  REACT_OPTIMISTIC_KEY,43} from 'shared/ReactSymbols';44import {45  HostRoot,46  HostText,47  HostPortal,48  Fragment,49  FunctionComponent,50} from './ReactWorkTags';51import isArray from 'shared/isArray';52import {53  enableAsyncIterableChildren,54  disableLegacyMode,55  enableFragmentRefs,56  enableOptimisticKey,57} from 'shared/ReactFeatureFlags';5859import {60  createWorkInProgress,61  resetWorkInProgress,62  createFiberFromElement,63  createFiberFromFragment,64  createFiberFromText,65  createFiberFromPortal,66  createFiberFromThrow,67} from './ReactFiber';68import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading';69import {getIsHydrating} from './ReactFiberHydrationContext';70import {pushTreeFork} from './ReactFiberTreeContext';71import {72  SuspenseException,73  SuspenseActionException,74  createThenableState,75  trackUsedThenable,76  resolveLazy,77} from './ReactFiberThenable';78import {readContextDuringReconciliation} from './ReactFiberNewContext';7980import {runWithFiberInDEV} from './ReactCurrentFiber';8182// This tracks the thenables that are unwrapped during reconcilation.83let thenableState: ThenableState | null = null;84let thenableIndexCounter: number = 0;8586// Server Components Meta Data87let currentDebugInfo: null | ReactDebugInfo = null;8889function pushDebugInfo(90  debugInfo: null | ReactDebugInfo,91): null | ReactDebugInfo {92  if (!__DEV__) {93    return null;94  }95  const previousDebugInfo = currentDebugInfo;96  if (debugInfo == null) {97    // Leave inplace98  } else if (previousDebugInfo === null) {99    currentDebugInfo = debugInfo;100  } else {101    // If we have two debugInfo, we need to create a new one. This makes the array no longer102    // live so we'll miss any future updates if we received more so ideally we should always103    // do this after both have fully resolved/unsuspended.104    currentDebugInfo = previousDebugInfo.concat(debugInfo);105  }106  return previousDebugInfo;107}108109function getCurrentDebugTask(): null | ConsoleTask {110  // Get the debug task of the parent Server Component if there is one.111  if (__DEV__) {112    const debugInfo = currentDebugInfo;113    if (debugInfo != null) {114      for (let i = debugInfo.length - 1; i >= 0; i--) {115        if (debugInfo[i].name != null) {116          const componentInfo: ReactComponentInfo = debugInfo[i];117          const debugTask: ?ConsoleTask = componentInfo.debugTask;118          if (debugTask != null) {119            return debugTask;120          }121        }122      }123    }124  }125  return null;126}127128let didWarnAboutMaps;129let didWarnAboutGenerators;130let ownerHasKeyUseWarning;131let ownerHasFunctionTypeWarning;132let ownerHasSymbolTypeWarning;133let warnForMissingKey = (134  returnFiber: Fiber,135  workInProgress: Fiber,136  child: mixed,137) => {};138139if (__DEV__) {140  didWarnAboutMaps = false;141  didWarnAboutGenerators = false;142143  /**144   * Warn if there's no key explicitly set on dynamic arrays of children or145   * object keys are not valid. This allows us to keep track of children between146   * updates.147   */148  ownerHasKeyUseWarning = {} as {[string]: boolean};149  ownerHasFunctionTypeWarning = {} as {[string]: boolean};150  ownerHasSymbolTypeWarning = {} as {[string]: boolean};151152  warnForMissingKey = (153    returnFiber: Fiber,154    workInProgress: Fiber,155    child: mixed,156  ) => {157    if (child === null || typeof child !== 'object') {158      return;159    }160    if (161      !child._store ||162      ((child._store.validated || child.key != null) &&163        child._store.validated !== 2)164    ) {165      return;166    }167168    if (typeof child._store !== 'object') {169      throw new Error(170        'React Component in warnForMissingKey should have a _store. ' +171          'This error is likely caused by a bug in React. Please file an issue.',172      );173    }174175    // $FlowFixMe[cannot-write] unable to narrow type from mixed to writable object176    child._store.validated = 1;177178    const componentName = getComponentNameFromFiber(returnFiber);179180    const componentKey = componentName || 'null';181    if (ownerHasKeyUseWarning[componentKey]) {182      return;183    }184    ownerHasKeyUseWarning[componentKey] = true;185186    const childOwner = child._owner;187    const parentOwner = returnFiber._debugOwner;188189    let currentComponentErrorInfo = '';190    if (parentOwner && typeof parentOwner.tag === 'number') {191      const name = getComponentNameFromFiber(parentOwner as any);192      if (name) {193        currentComponentErrorInfo =194          '\n\nCheck the render method of `' + name + '`.';195      }196    }197    if (!currentComponentErrorInfo) {198      if (componentName) {199        currentComponentErrorInfo = `\n\nCheck the top-level render call using <${componentName}>.`;200      }201    }202203    // Usually the current owner is the offender, but if it accepts children as a204    // property, it may be the creator of the child that's responsible for205    // assigning it a key.206    let childOwnerAppendix = '';207    if (childOwner != null && parentOwner !== childOwner) {208      let ownerName = null;209      if (typeof childOwner.tag === 'number') {210        ownerName = getComponentNameFromFiber(childOwner as any);211      } else if (typeof childOwner.name === 'string') {212        ownerName = childOwner.name;213      }214      if (ownerName) {215        // Give the component that originally created this child.216        childOwnerAppendix = ` It was passed a child from ${ownerName}.`;217      }218    }219220    runWithFiberInDEV(workInProgress, () => {221      console.error(222        'Each child in a list should have a unique "key" prop.' +223          '%s%s See https://react.dev/link/warning-keys for more information.',224        currentComponentErrorInfo,225        childOwnerAppendix,226      );227    });228  };229}230231// Given a fragment, validate that it can only be provided with fragment props232// We do this here instead of BeginWork because the Fragment fiber doesn't have233// the whole props object, only the children and is shared with arrays.234function validateFragmentProps(235  element: ReactElement,236  fiber: null | Fiber,237  returnFiber: Fiber,238) {239  if (__DEV__) {240    const keys = Object.keys(element.props);241    for (let i = 0; i < keys.length; i++) {242      const key = keys[i];243      if (244        key !== 'children' &&245        key !== 'key' &&246        (enableFragmentRefs ? key !== 'ref' : true)247      ) {248        if (fiber === null) {249          // For unkeyed root fragments without refs (enableFragmentRefs),250          // there's no Fiber. We create a fake one just for error stack handling.251          fiber = createFiberFromElement(element, returnFiber.mode, 0);252          if (__DEV__) {253            fiber._debugInfo = currentDebugInfo;254          }255          fiber.return = returnFiber;256        }257        runWithFiberInDEV(258          fiber,259          erroredKey => {260            if (enableFragmentRefs) {261              console.error(262                'Invalid prop `%s` supplied to `React.Fragment`. ' +263                  'React.Fragment can only have `key`, `ref`, and `children` props.',264                erroredKey,265              );266            } else {267              console.error(268                'Invalid prop `%s` supplied to `React.Fragment`. ' +269                  'React.Fragment can only have `key` and `children` props.',270                erroredKey,271              );272            }273          },274          key,275        );276        break;277      }278    }279  }280}281282function unwrapThenable<T>(thenable: Thenable<T>): T {283  const index = thenableIndexCounter;284  thenableIndexCounter += 1;285  if (thenableState === null) {286    thenableState = createThenableState();287  }288  return trackUsedThenable(thenableState, thenable, index, null);289}290291function coerceRef(workInProgress: Fiber, element: ReactElement): void {292  // TODO: This is a temporary, intermediate step. Now that enableRefAsProp is on,293  // we should resolve the `ref` prop during the begin phase of the component294  // it's attached to (HostComponent, ClassComponent, etc).295  const refProp = element.props.ref;296  // TODO: With enableRefAsProp now rolled out, we shouldn't use the `ref` field. We297  // should always read the ref from the prop.298  workInProgress.ref = refProp !== undefined ? refProp : null;299}300301function throwOnInvalidObjectTypeImpl(returnFiber: Fiber, newChild: Object) {302  if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE) {303    throw new Error(304      'A React Element from an older version of React was rendered. ' +305        'This is not supported. It can happen if:\n' +306        '- Multiple copies of the "react" package is used.\n' +307        '- A library pre-bundled an old copy of "react" or "react/jsx-runtime".\n' +308        '- A compiler tries to "inline" JSX instead of using the runtime.',309    );310  }311312  // $FlowFixMe[method-unbinding]313  const childString = Object.prototype.toString.call(newChild);314315  throw new Error(316    `Objects are not valid as a React child (found: ${317      childString === '[object Object]'318        ? 'object with keys {' + Object.keys(newChild).join(', ') + '}'319        : childString320    }). ` +321      'If you meant to render a collection of children, use an array ' +322      'instead.',323  );324}325326function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {327  const debugTask = getCurrentDebugTask();328  if (__DEV__ && debugTask !== null) {329    debugTask.run(330      throwOnInvalidObjectTypeImpl.bind(null, returnFiber, newChild),331    );332  } else {333    throwOnInvalidObjectTypeImpl(returnFiber, newChild);334  }335}336337function warnOnFunctionTypeImpl(returnFiber: Fiber, invalidChild: Function) {338  if (__DEV__) {339    const parentName = getComponentNameFromFiber(returnFiber) || 'Component';340341    if (ownerHasFunctionTypeWarning[parentName]) {342      return;343    }344    ownerHasFunctionTypeWarning[parentName] = true;345346    const name = invalidChild.displayName || invalidChild.name || 'Component';347348    if (returnFiber.tag === HostRoot) {349      console.error(350        'Functions are not valid as a React child. This may happen if ' +351          'you return %s instead of <%s /> from render. ' +352          'Or maybe you meant to call this function rather than return it.\n' +353          '  root.render(%s)',354        name,355        name,356        name,357      );358    } else {359      console.error(360        'Functions are not valid as a React child. This may happen if ' +361          'you return %s instead of <%s /> from render. ' +362          'Or maybe you meant to call this function rather than return it.\n' +363          '  <%s>{%s}</%s>',364        name,365        name,366        parentName,367        name,368        parentName,369      );370    }371  }372}373374function warnOnFunctionType(returnFiber: Fiber, invalidChild: Function) {375  const debugTask = getCurrentDebugTask();376  if (__DEV__ && debugTask !== null) {377    debugTask.run(warnOnFunctionTypeImpl.bind(null, returnFiber, invalidChild));378  } else {379    warnOnFunctionTypeImpl(returnFiber, invalidChild);380  }381}382383function warnOnSymbolTypeImpl(returnFiber: Fiber, invalidChild: symbol) {384  if (__DEV__) {385    const parentName = getComponentNameFromFiber(returnFiber) || 'Component';386387    if (ownerHasSymbolTypeWarning[parentName]) {388      return;389    }390    ownerHasSymbolTypeWarning[parentName] = true;391392    // eslint-disable-next-line react-internal/safe-string-coercion393    const name = String(invalidChild);394395    if (returnFiber.tag === HostRoot) {396      console.error(397        'Symbols are not valid as a React child.\n' + '  root.render(%s)',398        name,399      );400    } else {401      console.error(402        'Symbols are not valid as a React child.\n' + '  <%s>%s</%s>',403        parentName,404        name,405        parentName,406      );407    }408  }409}410411function warnOnSymbolType(returnFiber: Fiber, invalidChild: symbol) {412  const debugTask = getCurrentDebugTask();413  if (__DEV__ && debugTask !== null) {414    debugTask.run(warnOnSymbolTypeImpl.bind(null, returnFiber, invalidChild));415  } else {416    warnOnSymbolTypeImpl(returnFiber, invalidChild);417  }418}419420type ChildReconciler = (421  returnFiber: Fiber,422  currentFirstChild: Fiber | null,423  newChild: any,424  lanes: Lanes,425) => Fiber | null;426427// This wrapper function exists because I expect to clone the code in each path428// to be able to optimize each path individually by branching early. This needs429// a compiler or we can do it manually. Helpers that don't need this branching430// live outside of this function.431function createChildReconciler(432  shouldTrackSideEffects: boolean,433): ChildReconciler {434  function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {435    if (!shouldTrackSideEffects) {436      // Noop.437      return;438    }439    const deletions = returnFiber.deletions;440    if (deletions === null) {441      returnFiber.deletions = [childToDelete];442      returnFiber.flags |= ChildDeletion;443    } else {444      deletions.push(childToDelete);445    }446  }447448  function deleteRemainingChildren(449    returnFiber: Fiber,450    currentFirstChild: Fiber | null,451  ): null {452    if (!shouldTrackSideEffects) {453      // Noop.454      return null;455    }456457    // TODO: For the shouldClone case, this could be micro-optimized a bit by458    // assuming that after the first child we've already added everything.459    let childToDelete = currentFirstChild;460    while (childToDelete !== null) {461      deleteChild(returnFiber, childToDelete);462      childToDelete = childToDelete.sibling;463    }464    return null;465  }466467  function mapRemainingChildren(468    currentFirstChild: Fiber,469  ): Map<string | number | ReactOptimisticKey, Fiber> {470    // Add the remaining children to a temporary map so that we can find them by471    // keys quickly. Implicit (null) keys get added to this set with their index472    // instead.473    const existingChildren: Map<474      | string475      | number476      // This type is only here for the case when enableOptimisticKey is disabled.477      // Remove it after it ships.478      | ReactOptimisticKey,479      Fiber,480    > = new Map();481482    let existingChild: null | Fiber = currentFirstChild;483    while (existingChild !== null) {484      if (existingChild.key === null) {485        existingChildren.set(existingChild.index, existingChild);486      } else if (487        enableOptimisticKey &&488        existingChild.key === REACT_OPTIMISTIC_KEY489      ) {490        // For optimistic keys, we store the negative index (minus one) to differentiate491        // them from the regular indices. We'll look this up regardless of what the new492        // key is, if there's no other match.493        existingChildren.set(-existingChild.index - 1, existingChild);494      } else {495        existingChildren.set(existingChild.key, existingChild);496      }497      existingChild = existingChild.sibling;498    }499    return existingChildren;500  }501502  function useFiber(fiber: Fiber, pendingProps: mixed): Fiber {503    // We currently set sibling to null and index to 0 here because it is easy504    // to forget to do before returning it. E.g. for the single child case.505    const clone = createWorkInProgress(fiber, pendingProps);506    clone.index = 0;507    clone.sibling = null;508    return clone;509  }510511  function placeChild(512    newFiber: Fiber,513    lastPlacedIndex: number,514    newIndex: number,515  ): number {516    newFiber.index = newIndex;517    if (!shouldTrackSideEffects) {518      // During hydration, the useId algorithm needs to know which fibers are519      // part of a list of children (arrays, iterators).520      newFiber.flags |= Forked;521      return lastPlacedIndex;522    }523    const current = newFiber.alternate;524    if (current !== null) {525      const oldIndex = current.index;526      if (oldIndex < lastPlacedIndex) {527        // This is a move. The fiber already existed, so this is not a new528        // mount; don't set PlacementDEV, which would cause StrictMode to529        // re-run the effects in its subtree as if it had remounted.530        newFiber.flags |= Placement;531        return lastPlacedIndex;532      } else {533        // This item can stay in place.534        return oldIndex;535      }536    } else {537      // This is an insertion.538      newFiber.flags |= Placement | PlacementDEV;539      return lastPlacedIndex;540    }541  }542543  function placeSingleChild(newFiber: Fiber): Fiber {544    // This is simpler for the single child case. We only need to do a545    // placement for inserting new children.546    if (shouldTrackSideEffects && newFiber.alternate === null) {547      newFiber.flags |= Placement | PlacementDEV;548    }549    return newFiber;550  }551552  function updateTextNode(553    returnFiber: Fiber,554    current: Fiber | null,555    textContent: string,556    lanes: Lanes,557  ) {558    if (current === null || current.tag !== HostText) {559      // Insert560      const created = createFiberFromText(textContent, returnFiber.mode, lanes);561      created.return = returnFiber;562      if (__DEV__) {563        // We treat the parent as the owner for stack purposes.564        created._debugOwner = returnFiber;565        created._debugTask = returnFiber._debugTask;566        created._debugInfo = currentDebugInfo;567      }568      return created;569    } else {570      // Update571      const existing = useFiber(current, textContent);572      existing.return = returnFiber;573      if (__DEV__) {574        existing._debugInfo = currentDebugInfo;575      }576      return existing;577    }578  }579580  function updateElement(581    returnFiber: Fiber,582    current: Fiber | null,583    element: ReactElement,584    lanes: Lanes,585  ): Fiber {586    const elementType = element.type;587    if (elementType === REACT_FRAGMENT_TYPE) {588      const updated = updateFragment(589        returnFiber,590        current,591        element.props.children,592        lanes,593        element.key,594      );595      if (enableFragmentRefs) {596        coerceRef(updated, element);597      }598      validateFragmentProps(element, updated, returnFiber);599      return updated;600    }601    if (current !== null) {602      if (603        current.elementType === elementType ||604        // Keep this check inline so it only runs on the false path:605        (__DEV__606          ? isCompatibleFamilyForHotReloading(current, element)607          : false) ||608        // Lazy types should reconcile their resolved type.609        // We need to do this after the Hot Reloading check above,610        // because hot reloading has different semantics than prod because611        // it doesn't resuspend. So we can't let the call below suspend.612        (typeof elementType === 'object' &&613          elementType !== null &&614          elementType.$$typeof === REACT_LAZY_TYPE &&615          resolveLazy(elementType) === current.type)616      ) {617        // Move based on index618        const existing = useFiber(current, element.props);619        coerceRef(existing, element);620        existing.return = returnFiber;621        if (__DEV__) {622          existing._debugOwner = element._owner;623          existing._debugInfo = currentDebugInfo;624        }625        return existing;626      }627    }628    // Insert629    const created = createFiberFromElement(element, returnFiber.mode, lanes);630    coerceRef(created, element);631    created.return = returnFiber;632    if (__DEV__) {633      created._debugInfo = currentDebugInfo;634    }635    return created;636  }637638  function updatePortal(639    returnFiber: Fiber,640    current: Fiber | null,641    portal: ReactPortal,642    lanes: Lanes,643  ): Fiber {644    if (645      current === null ||646      current.tag !== HostPortal ||647      current.stateNode.containerInfo !== portal.containerInfo ||648      current.stateNode.implementation !== portal.implementation649    ) {650      // Insert651      const created = createFiberFromPortal(portal, returnFiber.mode, lanes);652      created.return = returnFiber;653      if (__DEV__) {654        created._debugInfo = currentDebugInfo;655      }656      return created;657    } else {658      // Update659      const existing = useFiber(current, portal.children || []);660      if (enableOptimisticKey) {661        // If the old key was optimistic we need to now save the real one.662        existing.key = portal.key;663      }664      existing.return = returnFiber;665      if (__DEV__) {666        existing._debugInfo = currentDebugInfo;667      }668      return existing;669    }670  }671672  function updateFragment(673    returnFiber: Fiber,674    current: Fiber | null,675    fragment: Iterable<React$Node>,676    lanes: Lanes,677    key: ReactKey,678  ): Fiber {679    if (current === null || current.tag !== Fragment) {680      // Insert681      const created = createFiberFromFragment(682        fragment,683        returnFiber.mode,684        lanes,685        key,686      );687      created.return = returnFiber;688      if (__DEV__) {689        // We treat the parent as the owner for stack purposes.690        created._debugOwner = returnFiber;691        created._debugTask = returnFiber._debugTask;692        created._debugInfo = currentDebugInfo;693      }694      return created;695    } else {696      // Update697      const existing = useFiber(current, fragment);698      if (enableOptimisticKey) {699        // If the old key was optimistic we need to now save the real one.700        existing.key = key;701      }702      existing.return = returnFiber;703      if (__DEV__) {704        existing._debugInfo = currentDebugInfo;705      }706      return existing;707    }708  }709710  function createChild(711    returnFiber: Fiber,712    newChild: any,713    lanes: Lanes,714  ): Fiber | null {715    if (716      (typeof newChild === 'string' && newChild !== '') ||717      typeof newChild === 'number' ||718      typeof newChild === 'bigint'719    ) {720      // Text nodes don't have keys. If the previous node is implicitly keyed721      // we can continue to replace it without aborting even if it is not a text722      // node.723      const created = createFiberFromText(724        // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint725        '' + newChild,726        returnFiber.mode,727        lanes,728      );729      created.return = returnFiber;730      if (__DEV__) {731        // We treat the parent as the owner for stack purposes.732        created._debugOwner = returnFiber;733        created._debugTask = returnFiber._debugTask;734        created._debugInfo = currentDebugInfo;735      }736      return created;737    }738739    if (typeof newChild === 'object' && newChild !== null) {740      switch (newChild.$$typeof) {741        case REACT_ELEMENT_TYPE: {742          const created = createFiberFromElement(743            newChild,744            returnFiber.mode,745            lanes,746          );747          coerceRef(created, newChild);748          created.return = returnFiber;749          if (__DEV__) {750            const prevDebugInfo = pushDebugInfo(newChild._debugInfo);751            created._debugInfo = currentDebugInfo;752            currentDebugInfo = prevDebugInfo;753          }754          return created;755        }756        case REACT_PORTAL_TYPE: {757          const created = createFiberFromPortal(758            newChild,759            returnFiber.mode,760            lanes,761          );762          created.return = returnFiber;763          if (__DEV__) {764            created._debugInfo = currentDebugInfo;765          }766          return created;767        }768        case REACT_LAZY_TYPE: {769          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);770          const resolvedChild = resolveLazy(newChild as any);771          const created = createChild(returnFiber, resolvedChild, lanes);772          currentDebugInfo = prevDebugInfo;773          return created;774        }775      }776777      if (778        isArray(newChild) ||779        getIteratorFn(newChild) ||780        (enableAsyncIterableChildren &&781          typeof newChild[ASYNC_ITERATOR] === 'function')782      ) {783        const created = createFiberFromFragment(784          newChild,785          returnFiber.mode,786          lanes,787          null,788        );789        created.return = returnFiber;790        if (__DEV__) {791          // We treat the parent as the owner for stack purposes.792          created._debugOwner = returnFiber;793          created._debugTask = returnFiber._debugTask;794          // Make sure to not push again when handling the Fragment child.795          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);796          created._debugInfo = currentDebugInfo;797          currentDebugInfo = prevDebugInfo;798        }799        return created;800      }801802      // Usable node types803      //804      // Unwrap the inner value and recursively call this function again.805      if (typeof newChild.then === 'function') {806        const thenable: Thenable<any> = newChild as any;807        const prevDebugInfo = pushDebugInfo(newChild._debugInfo);808        const created = createChild(809          returnFiber,810          unwrapThenable(thenable),811          lanes,812        );813        currentDebugInfo = prevDebugInfo;814        return created;815      }816817      // $FlowFixMe[invalid-compare]818      if (newChild.$$typeof === REACT_CONTEXT_TYPE) {819        const context: ReactContext<mixed> = newChild as any;820        return createChild(821          returnFiber,822          readContextDuringReconciliation(returnFiber, context, lanes),823          lanes,824        );825      }826827      throwOnInvalidObjectType(returnFiber, newChild);828    }829830    if (__DEV__) {831      if (typeof newChild === 'function') {832        warnOnFunctionType(returnFiber, newChild);833      }834      if (typeof newChild === 'symbol') {835        warnOnSymbolType(returnFiber, newChild);836      }837    }838839    return null;840  }841842  function updateSlot(843    returnFiber: Fiber,844    oldFiber: Fiber | null,845    newChild: any,846    lanes: Lanes,847  ): Fiber | null {848    // Update the fiber if the keys match, otherwise return null.849    const key = oldFiber !== null ? oldFiber.key : null;850851    if (852      (typeof newChild === 'string' && newChild !== '') ||853      typeof newChild === 'number' ||854      typeof newChild === 'bigint'855    ) {856      // Text nodes don't have keys. If the previous node is implicitly keyed857      // we can continue to replace it without aborting even if it is not a text858      // node.859      if (key !== null) {860        return null;861      }862      return updateTextNode(863        returnFiber,864        oldFiber,865        // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint866        '' + newChild,867        lanes,868      );869    }870871    if (typeof newChild === 'object' && newChild !== null) {872      switch (newChild.$$typeof) {873        case REACT_ELEMENT_TYPE: {874          if (875            // If the old child was an optimisticKey, then we'd normally consider that a match,876            // but instead, we'll bail to return null from the slot which will bail to slow path.877            // That's to ensure that if the new key has a match elsewhere in the list, then that878            // takes precedence over assuming the identity of an optimistic slot.879            newChild.key === key880          ) {881            const prevDebugInfo = pushDebugInfo(newChild._debugInfo);882            const updated = updateElement(883              returnFiber,884              oldFiber,885              newChild,886              lanes,887            );888            currentDebugInfo = prevDebugInfo;889            return updated;890          } else {891            return null;892          }893        }894        case REACT_PORTAL_TYPE: {895          if (896            // If the old child was an optimisticKey, then we'd normally consider that a match,897            // but instead, we'll bail to return null from the slot which will bail to slow path.898            // That's to ensure that if the new key has a match elsewhere in the list, then that899            // takes precedence over assuming the identity of an optimistic slot.900            newChild.key === key901          ) {902            return updatePortal(returnFiber, oldFiber, newChild, lanes);903          } else {904            return null;905          }906        }907        case REACT_LAZY_TYPE: {908          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);909          const resolvedChild = resolveLazy(newChild as any);910          const updated = updateSlot(911            returnFiber,912            oldFiber,913            resolvedChild,914            lanes,915          );916          currentDebugInfo = prevDebugInfo;917          return updated;918        }919      }920921      if (922        isArray(newChild) ||923        getIteratorFn(newChild) ||924        (enableAsyncIterableChildren &&925          typeof newChild[ASYNC_ITERATOR] === 'function')926      ) {927        if (key !== null) {928          return null;929        }930931        const prevDebugInfo = pushDebugInfo(newChild._debugInfo);932        const updated = updateFragment(933          returnFiber,934          oldFiber,935          newChild,936          lanes,937          null,938        );939        currentDebugInfo = prevDebugInfo;940        return updated;941      }942943      // Usable node types944      //945      // Unwrap the inner value and recursively call this function again.946      if (typeof newChild.then === 'function') {947        const thenable: Thenable<any> = newChild as any;948        const prevDebugInfo = pushDebugInfo((thenable as any)._debugInfo);949        const updated = updateSlot(950          returnFiber,951          oldFiber,952          unwrapThenable(thenable),953          lanes,954        );955        currentDebugInfo = prevDebugInfo;956        return updated;957      }958959      // $FlowFixMe[invalid-compare]960      if (newChild.$$typeof === REACT_CONTEXT_TYPE) {961        const context: ReactContext<mixed> = newChild as any;962        return updateSlot(963          returnFiber,964          oldFiber,965          readContextDuringReconciliation(returnFiber, context, lanes),966          lanes,967        );968      }969970      throwOnInvalidObjectType(returnFiber, newChild);971    }972973    if (__DEV__) {974      if (typeof newChild === 'function') {975        warnOnFunctionType(returnFiber, newChild);976      }977      if (typeof newChild === 'symbol') {978        warnOnSymbolType(returnFiber, newChild);979      }980    }981982    return null;983  }984985  function updateFromMap(986    existingChildren: Map<string | number | ReactOptimisticKey, Fiber>,987    returnFiber: Fiber,988    newIdx: number,989    newChild: any,990    lanes: Lanes,991  ): Fiber | null {992    if (993      (typeof newChild === 'string' && newChild !== '') ||994      typeof newChild === 'number' ||995      typeof newChild === 'bigint'996    ) {997      // Text nodes don't have keys, so we neither have to check the old nor998      // new node for the key. If both are text nodes, they match.999      const matchedFiber = existingChildren.get(newIdx) || null;1000      return updateTextNode(1001        returnFiber,1002        matchedFiber,1003        // $FlowFixMe[unsafe-addition] Flow doesn't want us to use `+` operator with string and bigint1004        '' + newChild,1005        lanes,1006      );1007    }10081009    if (typeof newChild === 'object' && newChild !== null) {1010      switch (newChild.$$typeof) {1011        case REACT_ELEMENT_TYPE: {1012          const matchedFiber =1013            existingChildren.get(1014              newChild.key === null ? newIdx : newChild.key,1015            ) ||1016            (enableOptimisticKey &&1017              // If the existing child was an optimistic key, we may still match on the index.1018              existingChildren.get(-newIdx - 1)) ||1019            null;1020          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);1021          const updated = updateElement(1022            returnFiber,1023            matchedFiber,1024            newChild,1025            lanes,1026          );1027          currentDebugInfo = prevDebugInfo;1028          return updated;1029        }1030        case REACT_PORTAL_TYPE: {1031          const matchedFiber =1032            existingChildren.get(1033              newChild.key === null ? newIdx : newChild.key,1034            ) ||1035            (enableOptimisticKey &&1036              // If the existing child was an optimistic key, we may still match on the index.1037              existingChildren.get(-newIdx - 1)) ||1038            null;1039          return updatePortal(returnFiber, matchedFiber, newChild, lanes);1040        }1041        case REACT_LAZY_TYPE: {1042          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);1043          const resolvedChild = resolveLazy(newChild as any);1044          const updated = updateFromMap(1045            existingChildren,1046            returnFiber,1047            newIdx,1048            resolvedChild,1049            lanes,1050          );1051          currentDebugInfo = prevDebugInfo;1052          return updated;1053        }1054      }10551056      if (1057        isArray(newChild) ||1058        getIteratorFn(newChild) ||1059        (enableAsyncIterableChildren &&1060          typeof newChild[ASYNC_ITERATOR] === 'function')1061      ) {1062        const matchedFiber = existingChildren.get(newIdx) || null;1063        const prevDebugInfo = pushDebugInfo(newChild._debugInfo);1064        const updated = updateFragment(1065          returnFiber,1066          matchedFiber,1067          newChild,1068          lanes,1069          null,1070        );1071        currentDebugInfo = prevDebugInfo;1072        return updated;1073      }10741075      // Usable node types1076      //1077      // Unwrap the inner value and recursively call this function again.1078      if (typeof newChild.then === 'function') {1079        const thenable: Thenable<any> = newChild as any;1080        const prevDebugInfo = pushDebugInfo((thenable as any)._debugInfo);1081        const updated = updateFromMap(1082          existingChildren,1083          returnFiber,1084          newIdx,1085          unwrapThenable(thenable),1086          lanes,1087        );1088        currentDebugInfo = prevDebugInfo;1089        return updated;1090      }10911092      // $FlowFixMe[invalid-compare]1093      if (newChild.$$typeof === REACT_CONTEXT_TYPE) {1094        const context: ReactContext<mixed> = newChild as any;1095        return updateFromMap(1096          existingChildren,1097          returnFiber,1098          newIdx,1099          readContextDuringReconciliation(returnFiber, context, lanes),1100          lanes,1101        );1102      }11031104      throwOnInvalidObjectType(returnFiber, newChild);1105    }11061107    if (__DEV__) {1108      if (typeof newChild === 'function') {1109        warnOnFunctionType(returnFiber, newChild);1110      }1111      if (typeof newChild === 'symbol') {1112        warnOnSymbolType(returnFiber, newChild);1113      }1114    }11151116    return null;1117  }11181119  /**1120   * Warns if there is a duplicate or missing key1121   */1122  function warnOnInvalidKey(1123    returnFiber: Fiber,1124    workInProgress: Fiber,1125    child: mixed,1126    knownKeys: Set<string> | null,1127  ): Set<string> | null {1128    if (__DEV__) {1129      if (typeof child !== 'object' || child === null) {1130        return knownKeys;1131      }1132      switch (child.$$typeof) {1133        case REACT_ELEMENT_TYPE:1134        case REACT_PORTAL_TYPE:1135          warnForMissingKey(returnFiber, workInProgress, child);1136          const key = child.key;1137          if (typeof key !== 'string') {1138            break;1139          }1140          if (knownKeys === null) {1141            knownKeys = new Set();1142            knownKeys.add(key);1143            break;1144          }1145          if (!knownKeys.has(key)) {1146            knownKeys.add(key);1147            break;1148          }1149          runWithFiberInDEV(workInProgress, () => {1150            console.error(1151              'Encountered two children with the same key, `%s`. ' +1152                'Keys should be unique so that components maintain their identity ' +1153                'across updates. Non-unique keys may cause children to be ' +1154                'duplicated and/or omitted — the behavior is unsupported and ' +1155                'could change in a future version.',1156              key,1157            );1158          });1159          break;1160        case REACT_LAZY_TYPE: {1161          const resolvedChild = resolveLazy(child as any);1162          warnOnInvalidKey(1163            returnFiber,1164            workInProgress,1165            resolvedChild,1166            knownKeys,1167          );1168          break;1169        }1170        default:1171          break;1172      }1173    }1174    return knownKeys;1175  }11761177  function reconcileChildrenArray(1178    returnFiber: Fiber,1179    currentFirstChild: Fiber | null,1180    newChildren: Array<any>,1181    lanes: Lanes,1182  ): Fiber | null {1183    // This algorithm can't optimize by searching from both ends since we1184    // don't have backpointers on fibers. I'm trying to see how far we can get1185    // with that model. If it ends up not being worth the tradeoffs, we can1186    // add it later.11871188    // Even with a two ended optimization, we'd want to optimize for the case1189    // where there are few changes and brute force the comparison instead of1190    // going for the Map. It'd like to explore hitting that path first in1191    // forward-only mode and only go for the Map once we notice that we need1192    // lots of look ahead. This doesn't handle reversal as well as two ended1193    // search but that's unusual. Besides, for the two ended optimization to1194    // work on Iterables, we'd need to copy the whole set.11951196    // In this first iteration, we'll just live with hitting the bad case1197    // (adding everything to a Map) in for every insert/move.11981199    // If you change this code, also update reconcileChildrenIterator() which1200    // uses the same algorithm.12011202    let knownKeys: Set<string> | null = null;12031204    let resultingFirstChild: Fiber | null = null;1205    let previousNewFiber: Fiber | null = null;12061207    let oldFiber = currentFirstChild;1208    let lastPlacedIndex = 0;1209    let newIdx = 0;1210    let nextOldFiber = null;1211    for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {1212      if (oldFiber.index > newIdx) {1213        nextOldFiber = oldFiber;1214        oldFiber = null;1215      } else {1216        nextOldFiber = oldFiber.sibling;1217      }1218      const newFiber = updateSlot(1219        returnFiber,1220        oldFiber,1221        newChildren[newIdx],1222        lanes,1223      );1224      if (newFiber === null) {1225        // TODO: This breaks on empty slots like null children. That's1226        // unfortunate because it triggers the slow path all the time. We need1227        // a better way to communicate whether this was a miss or null,1228        // boolean, undefined, etc.1229        if (oldFiber === null) {1230          oldFiber = nextOldFiber;1231        }1232        break;1233      }12341235      if (__DEV__) {1236        knownKeys = warnOnInvalidKey(1237          returnFiber,1238          newFiber,1239          newChildren[newIdx],1240          knownKeys,1241        );1242      }12431244      if (shouldTrackSideEffects) {1245        if (oldFiber && newFiber.alternate === null) {1246          // We matched the slot, but we didn't reuse the existing fiber, so we1247          // need to delete the existing child.1248          deleteChild(returnFiber, oldFiber);1249        }1250      }1251      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1252      if (previousNewFiber === null) {1253        // TODO: Move out of the loop. This only happens for the first run.1254        resultingFirstChild = newFiber;1255      } else {1256        // TODO: Defer siblings if we're not at the right index for this slot.1257        // I.e. if we had null values before, then we want to defer this1258        // for each null value. However, we also don't want to call updateSlot1259        // with the previous one.1260        previousNewFiber.sibling = newFiber;1261      }1262      previousNewFiber = newFiber;1263      oldFiber = nextOldFiber;1264    }12651266    if (newIdx === newChildren.length) {1267      // We've reached the end of the new children. We can delete the rest.1268      deleteRemainingChildren(returnFiber, oldFiber);1269      if (getIsHydrating()) {1270        const numberOfForks = newIdx;1271        pushTreeFork(returnFiber, numberOfForks);1272      }1273      return resultingFirstChild;1274    }12751276    if (oldFiber === null) {1277      // If we don't have any more existing children we can choose a fast path1278      // since the rest will all be insertions.1279      for (; newIdx < newChildren.length; newIdx++) {1280        const newFiber = createChild(returnFiber, newChildren[newIdx], lanes);1281        if (newFiber === null) {1282          continue;1283        }1284        if (__DEV__) {1285          knownKeys = warnOnInvalidKey(1286            returnFiber,1287            newFiber,1288            newChildren[newIdx],1289            knownKeys,1290          );1291        }1292        lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1293        if (previousNewFiber === null) {1294          // TODO: Move out of the loop. This only happens for the first run.1295          resultingFirstChild = newFiber;1296        } else {1297          previousNewFiber.sibling = newFiber;1298        }1299        previousNewFiber = newFiber;1300      }1301      if (getIsHydrating()) {1302        const numberOfForks = newIdx;1303        pushTreeFork(returnFiber, numberOfForks);1304      }1305      return resultingFirstChild;1306    }13071308    // Add all children to a key map for quick lookups.1309    const existingChildren = mapRemainingChildren(oldFiber);13101311    // Keep scanning and use the map to restore deleted items as moves.1312    for (; newIdx < newChildren.length; newIdx++) {1313      const newFiber = updateFromMap(1314        existingChildren,1315        returnFiber,1316        newIdx,1317        newChildren[newIdx],1318        lanes,1319      );1320      if (newFiber !== null) {1321        if (__DEV__) {1322          knownKeys = warnOnInvalidKey(1323            returnFiber,1324            newFiber,1325            newChildren[newIdx],1326            knownKeys,1327          );1328        }1329        if (shouldTrackSideEffects) {1330          const currentFiber = newFiber.alternate;1331          if (currentFiber !== null) {1332            // The new fiber is a work in progress, but if there exists a1333            // current, that means that we reused the fiber. We need to delete1334            // it from the child list so that we don't add it to the deletion1335            // list.1336            if (1337              enableOptimisticKey &&1338              currentFiber.key === REACT_OPTIMISTIC_KEY1339            ) {1340              existingChildren.delete(-newIdx - 1);1341            } else {1342              existingChildren.delete(1343                currentFiber.key === null ? newIdx : currentFiber.key,1344              );1345            }1346          }1347        }1348        lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1349        if (previousNewFiber === null) {1350          resultingFirstChild = newFiber;1351        } else {1352          previousNewFiber.sibling = newFiber;1353        }1354        previousNewFiber = newFiber;1355      }1356    }13571358    if (shouldTrackSideEffects) {1359      // Any existing children that weren't consumed above were deleted. We need1360      // to add them to the deletion list.1361      existingChildren.forEach(child => deleteChild(returnFiber, child));1362    }13631364    if (getIsHydrating()) {1365      const numberOfForks = newIdx;1366      pushTreeFork(returnFiber, numberOfForks);1367    }1368    return resultingFirstChild;1369  }13701371  function reconcileChildrenIteratable(1372    returnFiber: Fiber,1373    currentFirstChild: Fiber | null,1374    newChildrenIterable: Iterable<mixed>,1375    lanes: Lanes,1376  ): Fiber | null {1377    // This is the same implementation as reconcileChildrenArray(),1378    // but using the iterator instead.13791380    const iteratorFn = getIteratorFn(newChildrenIterable);13811382    if (typeof iteratorFn !== 'function') {1383      throw new Error(1384        'An object is not an iterable. This error is likely caused by a bug in ' +1385          'React. Please file an issue.',1386      );1387    }13881389    const newChildren = iteratorFn.call(newChildrenIterable);13901391    if (__DEV__) {1392      if (newChildren === newChildrenIterable) {1393        // We don't support rendering Generators as props because it's a mutation.1394        // See https://github.com/facebook/react/issues/129951395        // We do support generators if they were created by a GeneratorFunction component1396        // as its direct child since we can recreate those by rerendering the component1397        // as needed.1398        const isGeneratorComponent =1399          returnFiber.tag === FunctionComponent &&1400          // $FlowFixMe[method-unbinding]1401          Object.prototype.toString.call(returnFiber.type) ===1402            '[object GeneratorFunction]' &&1403          // $FlowFixMe[method-unbinding]1404          Object.prototype.toString.call(newChildren) === '[object Generator]';1405        if (!isGeneratorComponent) {1406          if (!didWarnAboutGenerators) {1407            console.error(1408              'Using Iterators as children is unsupported and will likely yield ' +1409                'unexpected results because enumerating a generator mutates it. ' +1410                'You may convert it to an array with `Array.from()` or the ' +1411                '`[...spread]` operator before rendering. You can also use an ' +1412                'Iterable that can iterate multiple times over the same items.',1413            );1414          }1415          didWarnAboutGenerators = true;1416        }1417      } else if ((newChildrenIterable as any).entries === iteratorFn) {1418        // Warn about using Maps as children1419        if (!didWarnAboutMaps) {1420          console.error(1421            'Using Maps as children is not supported. ' +1422              'Use an array of keyed ReactElements instead.',1423          );1424          didWarnAboutMaps = true;1425        }1426      }1427    }14281429    return reconcileChildrenIterator(1430      returnFiber,1431      currentFirstChild,1432      newChildren,1433      lanes,1434    );1435  }14361437  function reconcileChildrenAsyncIteratable(1438    returnFiber: Fiber,1439    currentFirstChild: Fiber | null,1440    newChildrenIterable: AsyncIterable<mixed>,1441    lanes: Lanes,1442  ): Fiber | null {1443    const newChildren = newChildrenIterable[ASYNC_ITERATOR]();14441445    if (__DEV__) {1446      if (newChildren === newChildrenIterable) {1447        // We don't support rendering AsyncGenerators as props because it's a mutation.1448        // We do support generators if they were created by a AsyncGeneratorFunction component1449        // as its direct child since we can recreate those by rerendering the component1450        // as needed.1451        const isGeneratorComponent =1452          returnFiber.tag === FunctionComponent &&1453          // $FlowFixMe[method-unbinding]1454          Object.prototype.toString.call(returnFiber.type) ===1455            '[object AsyncGeneratorFunction]' &&1456          // $FlowFixMe[method-unbinding]1457          Object.prototype.toString.call(newChildren) ===1458            '[object AsyncGenerator]';1459        if (!isGeneratorComponent) {1460          if (!didWarnAboutGenerators) {1461            console.error(1462              'Using AsyncIterators as children is unsupported and will likely yield ' +1463                'unexpected results because enumerating a generator mutates it. ' +1464                'You can use an AsyncIterable that can iterate multiple times over ' +1465                'the same items.',1466            );1467          }1468          didWarnAboutGenerators = true;1469        }1470      }1471    }14721473    if (newChildren == null) {1474      throw new Error('An iterable object provided no iterator.');1475    }14761477    // To save bytes, we reuse the logic by creating a synchronous Iterable and1478    // reusing that code path.1479    const iterator: Iterator<mixed> = {1480      next(): IteratorResult<mixed, void> {1481        return unwrapThenable(newChildren.next());1482      },1483    } as any;14841485    return reconcileChildrenIterator(1486      returnFiber,1487      currentFirstChild,1488      iterator,1489      lanes,1490    );1491  }14921493  function reconcileChildrenIterator(1494    returnFiber: Fiber,1495    currentFirstChild: Fiber | null,1496    newChildren: ?Iterator<mixed>,1497    lanes: Lanes,1498  ): Fiber | null {1499    if (newChildren == null) {1500      throw new Error('An iterable object provided no iterator.');1501    }15021503    let resultingFirstChild: Fiber | null = null;1504    let previousNewFiber: Fiber | null = null;15051506    let oldFiber = currentFirstChild;1507    let lastPlacedIndex = 0;1508    let newIdx = 0;1509    let nextOldFiber = null;15101511    let knownKeys: Set<string> | null = null;15121513    let step = newChildren.next();1514    for (1515      ;1516      oldFiber !== null && !step.done;1517      newIdx++, step = newChildren.next()1518    ) {1519      if (oldFiber.index > newIdx) {1520        nextOldFiber = oldFiber;1521        oldFiber = null;1522      } else {1523        nextOldFiber = oldFiber.sibling;1524      }1525      const newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);1526      if (newFiber === null) {1527        // TODO: This breaks on empty slots like null children. That's1528        // unfortunate because it triggers the slow path all the time. We need1529        // a better way to communicate whether this was a miss or null,1530        // boolean, undefined, etc.1531        if (oldFiber === null) {1532          oldFiber = nextOldFiber;1533        }1534        break;1535      }15361537      if (__DEV__) {1538        knownKeys = warnOnInvalidKey(1539          returnFiber,1540          newFiber,1541          step.value,1542          knownKeys,1543        );1544      }15451546      if (shouldTrackSideEffects) {1547        if (oldFiber && newFiber.alternate === null) {1548          // We matched the slot, but we didn't reuse the existing fiber, so we1549          // need to delete the existing child.1550          deleteChild(returnFiber, oldFiber);1551        }1552      }1553      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1554      if (previousNewFiber === null) {1555        // TODO: Move out of the loop. This only happens for the first run.1556        resultingFirstChild = newFiber;1557      } else {1558        // TODO: Defer siblings if we're not at the right index for this slot.1559        // I.e. if we had null values before, then we want to defer this1560        // for each null value. However, we also don't want to call updateSlot1561        // with the previous one.1562        previousNewFiber.sibling = newFiber;1563      }1564      previousNewFiber = newFiber;1565      oldFiber = nextOldFiber;1566    }15671568    if (step.done) {1569      // We've reached the end of the new children. We can delete the rest.1570      deleteRemainingChildren(returnFiber, oldFiber);1571      if (getIsHydrating()) {1572        const numberOfForks = newIdx;1573        pushTreeFork(returnFiber, numberOfForks);1574      }1575      return resultingFirstChild;1576    }15771578    if (oldFiber === null) {1579      // If we don't have any more existing children we can choose a fast path1580      // since the rest will all be insertions.1581      for (; !step.done; newIdx++, step = newChildren.next()) {1582        const newFiber = createChild(returnFiber, step.value, lanes);1583        if (newFiber === null) {1584          continue;1585        }1586        if (__DEV__) {1587          knownKeys = warnOnInvalidKey(1588            returnFiber,1589            newFiber,1590            step.value,1591            knownKeys,1592          );1593        }1594        lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1595        if (previousNewFiber === null) {1596          // TODO: Move out of the loop. This only happens for the first run.1597          resultingFirstChild = newFiber;1598        } else {1599          previousNewFiber.sibling = newFiber;1600        }1601        previousNewFiber = newFiber;1602      }1603      if (getIsHydrating()) {1604        const numberOfForks = newIdx;1605        pushTreeFork(returnFiber, numberOfForks);1606      }1607      return resultingFirstChild;1608    }16091610    // Add all children to a key map for quick lookups.1611    const existingChildren = mapRemainingChildren(oldFiber);16121613    // Keep scanning and use the map to restore deleted items as moves.1614    for (; !step.done; newIdx++, step = newChildren.next()) {1615      const newFiber = updateFromMap(1616        existingChildren,1617        returnFiber,1618        newIdx,1619        step.value,1620        lanes,1621      );1622      if (newFiber !== null) {1623        if (__DEV__) {1624          knownKeys = warnOnInvalidKey(1625            returnFiber,1626            newFiber,1627            step.value,1628            knownKeys,1629          );1630        }1631        if (shouldTrackSideEffects) {1632          const currentFiber = newFiber.alternate;1633          if (currentFiber !== null) {1634            // The new fiber is a work in progress, but if there exists a1635            // current, that means that we reused the fiber. We need to delete1636            // it from the child list so that we don't add it to the deletion1637            // list.1638            if (1639              enableOptimisticKey &&1640              currentFiber.key === REACT_OPTIMISTIC_KEY1641            ) {1642              existingChildren.delete(-newIdx - 1);1643            } else {1644              existingChildren.delete(1645                currentFiber.key === null ? newIdx : currentFiber.key,1646              );1647            }1648          }1649        }1650        lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);1651        if (previousNewFiber === null) {1652          resultingFirstChild = newFiber;1653        } else {1654          previousNewFiber.sibling = newFiber;1655        }1656        previousNewFiber = newFiber;1657      }1658    }16591660    if (shouldTrackSideEffects) {1661      // Any existing children that weren't consumed above were deleted. We need1662      // to add them to the deletion list.1663      existingChildren.forEach(child => deleteChild(returnFiber, child));1664    }16651666    if (getIsHydrating()) {1667      const numberOfForks = newIdx;1668      pushTreeFork(returnFiber, numberOfForks);1669    }1670    return resultingFirstChild;1671  }16721673  function reconcileSingleTextNode(1674    returnFiber: Fiber,1675    currentFirstChild: Fiber | null,1676    textContent: string,1677    lanes: Lanes,1678  ): Fiber {1679    // There's no need to check for keys on text nodes since we don't have a1680    // way to define them.1681    if (currentFirstChild !== null && currentFirstChild.tag === HostText) {1682      // We already have an existing node so let's just update it and delete1683      // the rest.1684      deleteRemainingChildren(returnFiber, currentFirstChild.sibling);1685      const existing = useFiber(currentFirstChild, textContent);1686      existing.return = returnFiber;1687      return existing;1688    }1689    // The existing first child is not a text node so we need to create one1690    // and delete the existing ones.1691    deleteRemainingChildren(returnFiber, currentFirstChild);1692    const created = createFiberFromText(textContent, returnFiber.mode, lanes);1693    created.return = returnFiber;1694    if (__DEV__) {1695      // We treat the parent as the owner for stack purposes.1696      created._debugOwner = returnFiber;1697      created._debugTask = returnFiber._debugTask;1698      created._debugInfo = currentDebugInfo;1699    }1700    return created;1701  }17021703  function reconcileSingleElement(1704    returnFiber: Fiber,1705    currentFirstChild: Fiber | null,1706    element: ReactElement,1707    lanes: Lanes,1708  ): Fiber {1709    const key = element.key;1710    let child = currentFirstChild;1711    while (child !== null) {1712      // TODO: If key === null and child.key === null, then this only applies to1713      // the first item in the list.1714      if (1715        child.key === key ||1716        (enableOptimisticKey && child.key === REACT_OPTIMISTIC_KEY)1717      ) {1718        const elementType = element.type;1719        if (elementType === REACT_FRAGMENT_TYPE) {1720          if (child.tag === Fragment) {1721            deleteRemainingChildren(returnFiber, child.sibling);1722            const existing = useFiber(child, element.props.children);1723            if (enableOptimisticKey) {1724              // If the old key was optimistic we need to now save the real one.1725              existing.key = key;1726            }1727            if (enableFragmentRefs) {1728              coerceRef(existing, element);1729            }1730            existing.return = returnFiber;1731            if (__DEV__) {1732              existing._debugOwner = element._owner;1733              existing._debugInfo = currentDebugInfo;1734            }1735            validateFragmentProps(element, existing, returnFiber);1736            return existing;1737          }1738        } else {1739          if (1740            child.elementType === elementType ||1741            // Keep this check inline so it only runs on the false path:1742            (__DEV__1743              ? isCompatibleFamilyForHotReloading(child, element)1744              : false) ||1745            // Lazy types should reconcile their resolved type.1746            // We need to do this after the Hot Reloading check above,1747            // because hot reloading has different semantics than prod because1748            // it doesn't resuspend. So we can't let the call below suspend.1749            (typeof elementType === 'object' &&1750              elementType !== null &&1751              elementType.$$typeof === REACT_LAZY_TYPE &&1752              resolveLazy(elementType) === child.type)1753          ) {1754            deleteRemainingChildren(returnFiber, child.sibling);1755            const existing = useFiber(child, element.props);1756            if (enableOptimisticKey) {1757              // If the old key was optimistic we need to now save the real one.1758              existing.key = key;1759            }1760            coerceRef(existing, element);1761            existing.return = returnFiber;1762            if (__DEV__) {1763              existing._debugOwner = element._owner;1764              existing._debugInfo = currentDebugInfo;1765            }1766            return existing;1767          }1768        }1769        // Didn't match.1770        deleteRemainingChildren(returnFiber, child);1771        break;1772      } else {1773        deleteChild(returnFiber, child);1774      }1775      child = child.sibling;1776    }17771778    if (element.type === REACT_FRAGMENT_TYPE) {1779      const created = createFiberFromFragment(1780        element.props.children,1781        returnFiber.mode,1782        lanes,1783        element.key,1784      );1785      if (enableFragmentRefs) {1786        coerceRef(created, element);1787      }1788      created.return = returnFiber;1789      if (__DEV__) {1790        // We treat the parent as the owner for stack purposes.1791        created._debugOwner = returnFiber;1792        created._debugTask = returnFiber._debugTask;1793        created._debugInfo = currentDebugInfo;1794      }1795      validateFragmentProps(element, created, returnFiber);1796      return created;1797    } else {1798      const created = createFiberFromElement(element, returnFiber.mode, lanes);1799      coerceRef(created, element);1800      created.return = returnFiber;1801      if (__DEV__) {1802        created._debugInfo = currentDebugInfo;1803      }1804      return created;1805    }1806  }18071808  function reconcileSinglePortal(1809    returnFiber: Fiber,1810    currentFirstChild: Fiber | null,1811    portal: ReactPortal,1812    lanes: Lanes,1813  ): Fiber {1814    const key = portal.key;1815    let child = currentFirstChild;1816    while (child !== null) {1817      // TODO: If key === null and child.key === null, then this only applies to1818      // the first item in the list.1819      if (1820        child.key === key ||1821        (enableOptimisticKey && child.key === REACT_OPTIMISTIC_KEY)1822      ) {1823        if (1824          child.tag === HostPortal &&1825          child.stateNode.containerInfo === portal.containerInfo &&1826          child.stateNode.implementation === portal.implementation1827        ) {1828          deleteRemainingChildren(returnFiber, child.sibling);1829          const existing = useFiber(child, portal.children || []);1830          if (enableOptimisticKey) {1831            // If the old key was optimistic we need to now save the real one.1832            existing.key = key;1833          }1834          existing.return = returnFiber;1835          return existing;1836        } else {1837          deleteRemainingChildren(returnFiber, child);1838          break;1839        }1840      } else {1841        deleteChild(returnFiber, child);1842      }1843      child = child.sibling;1844    }18451846    const created = createFiberFromPortal(portal, returnFiber.mode, lanes);1847    created.return = returnFiber;1848    return created;1849  }18501851  // This API will tag the children with the side-effect of the reconciliation1852  // itself. They will be added to the side-effect list as we pass through the1853  // children and the parent.1854  function reconcileChildFibersImpl(1855    returnFiber: Fiber,1856    currentFirstChild: Fiber | null,1857    newChild: any,1858    lanes: Lanes,1859  ): Fiber | null {1860    // This function is only recursive for Usables/Lazy and not nested arrays.1861    // That's so that using a Lazy wrapper is unobservable to the Fragment1862    // convention.1863    // If the top level item is an array, we treat it as a set of children,1864    // not as a fragment. Nested arrays on the other hand will be treated as1865    // fragment nodes. Recursion happens at the normal flow.18661867    // Handle top level unkeyed fragments without refs (enableFragmentRefs)1868    // as if they were arrays. This leads to an ambiguity between <>{[...]}</> and <>...</>.1869    // We treat the ambiguous cases above the same.1870    // We don't use recursion here because a fragment inside a fragment1871    // is no longer considered "top level" for these purposes.1872    const isUnkeyedUnrefedTopLevelFragment =1873      typeof newChild === 'object' &&1874      newChild !== null &&1875      newChild.type === REACT_FRAGMENT_TYPE &&1876      newChild.key === null &&1877      (enableFragmentRefs ? newChild.props.ref === undefined : true);18781879    if (isUnkeyedUnrefedTopLevelFragment) {1880      validateFragmentProps(newChild, null, returnFiber);1881      newChild = newChild.props.children;1882    }18831884    // Handle object types1885    if (typeof newChild === 'object' && newChild !== null) {1886      switch (newChild.$$typeof) {1887        case REACT_ELEMENT_TYPE: {1888          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);1889          const firstChild = placeSingleChild(1890            reconcileSingleElement(1891              returnFiber,1892              currentFirstChild,1893              newChild,1894              lanes,1895            ),1896          );1897          currentDebugInfo = prevDebugInfo;1898          return firstChild;1899        }1900        case REACT_PORTAL_TYPE:1901          return placeSingleChild(1902            reconcileSinglePortal(1903              returnFiber,1904              currentFirstChild,1905              newChild,1906              lanes,1907            ),1908          );1909        case REACT_LAZY_TYPE: {1910          const prevDebugInfo = pushDebugInfo(newChild._debugInfo);1911          const result = resolveLazy(newChild as any);1912          const firstChild = reconcileChildFibersImpl(1913            returnFiber,1914            currentFirstChild,1915            result,1916            lanes,1917          );1918          currentDebugInfo = prevDebugInfo;1919          return firstChild;1920        }1921      }19221923      if (isArray(newChild)) {1924        // We created a Fragment for this child with the debug info.1925        // No need to push again.1926        const firstChild = reconcileChildrenArray(1927          returnFiber,1928          currentFirstChild,1929          newChild,1930          lanes,1931        );1932        return firstChild;1933      }19341935      if (getIteratorFn(newChild)) {1936        // We created a Fragment for this child with the debug info.1937        // No need to push again.1938        const firstChild = reconcileChildrenIteratable(1939          returnFiber,1940          currentFirstChild,1941          newChild,1942          lanes,1943        );1944        return firstChild;1945      }19461947      if (1948        enableAsyncIterableChildren &&1949        typeof newChild[ASYNC_ITERATOR] === 'function'1950      ) {1951        // We created a Fragment for this child with the debug info.1952        // No need to push again.1953        const firstChild = reconcileChildrenAsyncIteratable(1954          returnFiber,1955          currentFirstChild,1956          newChild,1957          lanes,1958        );1959        return firstChild;1960      }19611962      // Usables are a valid React node type. When React encounters a Usable in1963      // a child position, it unwraps it using the same algorithm as `use`. For1964      // example, for promises, React will throw an exception to unwind the1965      // stack, then replay the component once the promise resolves.1966      //1967      // A difference from `use` is that React will keep unwrapping the value1968      // until it reaches a non-Usable type.1969      //1970      // e.g. Usable<Usable<Usable<T>>> should resolve to T1971      //1972      // The structure is a bit unfortunate. Ideally, we shouldn't need to1973      // replay the entire begin phase of the parent fiber in order to reconcile1974      // the children again. This would require a somewhat significant refactor,1975      // because reconcilation happens deep within the begin phase, and1976      // depending on the type of work, not always at the end. We should1977      // consider as an future improvement.1978      if (typeof newChild.then === 'function') {1979        const thenable: Thenable<any> = newChild as any;1980        const prevDebugInfo = pushDebugInfo((thenable as any)._debugInfo);1981        const firstChild = reconcileChildFibersImpl(1982          returnFiber,1983          currentFirstChild,1984          unwrapThenable(thenable),1985          lanes,1986        );1987        currentDebugInfo = prevDebugInfo;1988        return firstChild;1989      }19901991      // $FlowFixMe[invalid-compare]1992      if (newChild.$$typeof === REACT_CONTEXT_TYPE) {1993        const context: ReactContext<mixed> = newChild as any;1994        return reconcileChildFibersImpl(1995          returnFiber,1996          currentFirstChild,1997          readContextDuringReconciliation(returnFiber, context, lanes),1998          lanes,1999        );2000      }

Code quality findings 100

Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (debugInfo == null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (previousDebugInfo === null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (debugInfo != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (debugInfo[i].name != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (debugTask != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (child === null || typeof child !== 'object') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (child === null || typeof child !== 'object') {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
((child._store.validated || child.key != null) &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
child._store.validated !== 2)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof child._store !== 'object') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof child._store !== 'object') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (parentOwner && typeof parentOwner.tag === 'number') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (parentOwner && typeof parentOwner.tag === 'number') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (childOwner != null && parentOwner !== childOwner) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (childOwner != null && parentOwner !== childOwner) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof childOwner.tag === 'number') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof childOwner.tag === 'number') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (typeof childOwner.name === 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
} else if (typeof childOwner.name === 'string') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
key !== 'children' &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
key !== 'key' &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(enableFragmentRefs ? key !== 'ref' : true)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (fiber === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (thenableState === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
workInProgress.ref = refProp !== undefined ? refProp : null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (newChild.$$typeof === REACT_LEGACY_ELEMENT_TYPE) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
childString === '[object Object]'
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (__DEV__ && debugTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (returnFiber.tag === HostRoot) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (__DEV__ && debugTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (returnFiber.tag === HostRoot) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (__DEV__ && debugTask !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (deletions === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (childToDelete !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
while (existingChild !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (existingChild.key === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
existingChild.key === REACT_OPTIMISTIC_KEY
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (shouldTrackSideEffects && newFiber.alternate === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null || current.tag !== HostText) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (elementType === REACT_FRAGMENT_TYPE) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
current.elementType === elementType ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(typeof elementType === 'object' &&
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
(typeof elementType === 'object' &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
elementType !== null &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
elementType.$$typeof === REACT_LAZY_TYPE &&
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
elementType.$$typeof === REACT_LAZY_TYPE &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
resolveLazy(elementType) === current.type)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
current === null ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
current.tag !== HostPortal ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
current.stateNode.containerInfo !== portal.containerInfo ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
current.stateNode.implementation !== portal.implementation
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (current === null || current.tag !== Fragment) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(typeof newChild === 'string' && newChild !== '') ||
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
(typeof newChild === 'string' && newChild !== '') ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild === 'number' ||
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild === 'number' ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild === 'bigint'
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild === 'bigint'
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'object' && newChild !== null) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'object' && newChild !== null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (newChild.$$typeof) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild[ASYNC_ITERATOR] === 'function')
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild[ASYNC_ITERATOR] === 'function')
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild.then === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild.then === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'symbol') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'symbol') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const key = oldFiber !== null ? oldFiber.key : null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(typeof newChild === 'string' && newChild !== '') ||
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
(typeof newChild === 'string' && newChild !== '') ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild === 'number' ||
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild === 'number' ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild === 'bigint'
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild === 'bigint'
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (key !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'object' && newChild !== null) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'object' && newChild !== null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (newChild.$$typeof) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
newChild.key === key
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
newChild.key === key
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
typeof newChild[ASYNC_ITERATOR] === 'function')
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
typeof newChild[ASYNC_ITERATOR] === 'function')
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (key !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild.then === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild.then === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (newChild.$$typeof === REACT_CONTEXT_TYPE) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof newChild === 'symbol') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof newChild === 'symbol') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(typeof newChild === 'string' && newChild !== '') ||
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
(typeof newChild === 'string' && newChild !== '') ||

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.