packages/react-reconciler/src/ReactFiberCommitEffects.js JAVASCRIPT 1,046 lines View on github.com → Search inside
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 {11  ViewTransitionProps,12  ProfilerProps,13  ProfilerPhase,14} from 'shared/ReactTypes';15import type {Fiber} from './ReactInternalTypes';16import type {UpdateQueue} from './ReactFiberClassUpdateQueue';17import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';18import type {HookFlags} from './ReactHookEffectTags';19import type {FragmentInstanceType} from './ReactFiberConfig';20import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';2122import {getViewTransitionName} from './ReactFiberViewTransitionComponent';2324import {25  enableProfilerTimer,26  enableProfilerCommitHooks,27  enableProfilerNestedUpdatePhase,28  enableSchedulingProfiler,29  enableViewTransition,30  enableFragmentRefs,31} from 'shared/ReactFeatureFlags';32import {33  ClassComponent,34  Fragment,35  HostComponent,36  HostHoistable,37  HostSingleton,38  ViewTransitionComponent,39} from './ReactWorkTags';40import {NoFlags} from './ReactFiberFlags';41import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';42import {resolveClassComponentProps} from './ReactFiberClassComponent';43import {44  recordEffectDuration,45  startEffectTimer,46  isCurrentUpdateNested,47} from './ReactProfilerTimer';48import {NoMode, ProfileMode} from './ReactTypeOfMode';49import {50  commitCallbacks,51  commitHiddenCallbacks,52} from './ReactFiberClassUpdateQueue';53import {54  getPublicInstance,55  createViewTransitionInstance,56  createFragmentInstance,57} from './ReactFiberConfig';58import {59  captureCommitPhaseError,60  setIsRunningInsertionEffect,61} from './ReactFiberWorkLoop';62import {63  NoFlags as NoHookEffect,64  Layout as HookLayout,65  Insertion as HookInsertion,66  Passive as HookPassive,67} from './ReactHookEffectTags';68import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';69import {70  markComponentPassiveEffectMountStarted,71  markComponentPassiveEffectMountStopped,72  markComponentPassiveEffectUnmountStarted,73  markComponentPassiveEffectUnmountStopped,74  markComponentLayoutEffectMountStarted,75  markComponentLayoutEffectMountStopped,76  markComponentLayoutEffectUnmountStarted,77  markComponentLayoutEffectUnmountStopped,78} from './ReactFiberDevToolsHook';79import {80  callComponentDidMountInDEV,81  callComponentDidUpdateInDEV,82  callComponentWillUnmountInDEV,83  callCreateInDEV,84  callDestroyInDEV,85} from './ReactFiberCallUserSpace';8687import {runWithFiberInDEV} from './ReactCurrentFiber';8889function shouldProfile(current: Fiber): boolean {90  return (91    enableProfilerTimer &&92    enableProfilerCommitHooks &&93    (current.mode & ProfileMode) !== NoMode94  );95}9697export function commitHookLayoutEffects(98  finishedWork: Fiber,99  hookFlags: HookFlags,100) {101  // At this point layout effects have already been destroyed (during mutation phase).102  // This is done to prevent sibling component effects from interfering with each other,103  // e.g. a destroy function in one component should never override a ref set104  // by a create function in another component during the same commit.105  if (shouldProfile(finishedWork)) {106    startEffectTimer();107    commitHookEffectListMount(hookFlags, finishedWork);108    recordEffectDuration(finishedWork);109  } else {110    commitHookEffectListMount(hookFlags, finishedWork);111  }112}113114export function commitHookLayoutUnmountEffects(115  finishedWork: Fiber,116  nearestMountedAncestor: null | Fiber,117  hookFlags: HookFlags,118) {119  // Layout effects are destroyed during the mutation phase so that all120  // destroy functions for all fibers are called before any create functions.121  // This prevents sibling component effects from interfering with each other,122  // e.g. a destroy function in one component should never override a ref set123  // by a create function in another component during the same commit.124  if (shouldProfile(finishedWork)) {125    startEffectTimer();126    commitHookEffectListUnmount(127      hookFlags,128      finishedWork,129      nearestMountedAncestor,130    );131    recordEffectDuration(finishedWork);132  } else {133    commitHookEffectListUnmount(134      hookFlags,135      finishedWork,136      nearestMountedAncestor,137    );138  }139}140141export function commitHookEffectListMount(142  flags: HookFlags,143  finishedWork: Fiber,144) {145  try {146    const updateQueue: FunctionComponentUpdateQueue | null =147      finishedWork.updateQueue as any;148    const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;149    if (lastEffect !== null) {150      const firstEffect = lastEffect.next;151      let effect = firstEffect;152      do {153        if ((effect.tag & flags) === flags) {154          if (enableSchedulingProfiler) {155            if ((flags & HookPassive) !== NoHookEffect) {156              markComponentPassiveEffectMountStarted(finishedWork);157            } else if ((flags & HookLayout) !== NoHookEffect) {158              markComponentLayoutEffectMountStarted(finishedWork);159            }160          }161162          // Mount163          let destroy;164          if (__DEV__) {165            if ((flags & HookInsertion) !== NoHookEffect) {166              setIsRunningInsertionEffect(true);167            }168            destroy = runWithFiberInDEV(finishedWork, callCreateInDEV, effect);169            if ((flags & HookInsertion) !== NoHookEffect) {170              setIsRunningInsertionEffect(false);171            }172          } else {173            const create = effect.create;174            const inst = effect.inst;175            destroy = create();176            inst.destroy = destroy;177          }178179          if (enableSchedulingProfiler) {180            if ((flags & HookPassive) !== NoHookEffect) {181              markComponentPassiveEffectMountStopped();182            } else if ((flags & HookLayout) !== NoHookEffect) {183              markComponentLayoutEffectMountStopped();184            }185          }186187          if (__DEV__) {188            if (destroy !== undefined && typeof destroy !== 'function') {189              let hookName;190              if ((effect.tag & HookLayout) !== NoFlags) {191                hookName = 'useLayoutEffect';192              } else if ((effect.tag & HookInsertion) !== NoFlags) {193                hookName = 'useInsertionEffect';194              } else {195                hookName = 'useEffect';196              }197              let addendum;198              // $FlowFixMe[invalid-compare]199              if (destroy === null) {200                addendum =201                  ' You returned null. If your effect does not require clean ' +202                  'up, return undefined (or nothing).';203                // $FlowFixMe[incompatible-type] (@poteto) this check is safe on arbitrary non-null/void objects204              } else if (typeof destroy.then === 'function') {205                addendum =206                  '\n\nIt looks like you wrote ' +207                  hookName +208                  '(async () => ...) or returned a Promise. ' +209                  'Instead, write the async function inside your effect ' +210                  'and call it immediately:\n\n' +211                  hookName +212                  '(() => {\n' +213                  '  async function fetchData() {\n' +214                  '    // You can await here\n' +215                  '    const response = await MyAPI.getData(someId);\n' +216                  '    // ...\n' +217                  '  }\n' +218                  '  fetchData();\n' +219                  `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +220                  'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';221              } else {222                // $FlowFixMe[unsafe-addition] (@poteto)223                addendum = ' You returned: ' + destroy;224              }225              runWithFiberInDEV(226                finishedWork,227                (n, a) => {228                  console.error(229                    '%s must not return anything besides a function, ' +230                      'which is used for clean-up.%s',231                    n,232                    a,233                  );234                },235                hookName,236                addendum,237              );238            }239          }240        }241        effect = effect.next;242      } while (effect !== firstEffect);243    }244  } catch (error) {245    captureCommitPhaseError(finishedWork, finishedWork.return, error);246  }247}248249export function commitHookEffectListUnmount(250  flags: HookFlags,251  finishedWork: Fiber,252  nearestMountedAncestor: Fiber | null,253) {254  try {255    const updateQueue: FunctionComponentUpdateQueue | null =256      finishedWork.updateQueue as any;257    const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;258    if (lastEffect !== null) {259      const firstEffect = lastEffect.next;260      let effect = firstEffect;261      do {262        if ((effect.tag & flags) === flags) {263          // Unmount264          const inst = effect.inst;265          const destroy = inst.destroy;266          if (destroy !== undefined) {267            inst.destroy = undefined;268            if (enableSchedulingProfiler) {269              if ((flags & HookPassive) !== NoHookEffect) {270                markComponentPassiveEffectUnmountStarted(finishedWork);271              } else if ((flags & HookLayout) !== NoHookEffect) {272                markComponentLayoutEffectUnmountStarted(finishedWork);273              }274            }275276            if (__DEV__) {277              if ((flags & HookInsertion) !== NoHookEffect) {278                setIsRunningInsertionEffect(true);279              }280            }281            safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);282            if (__DEV__) {283              if ((flags & HookInsertion) !== NoHookEffect) {284                setIsRunningInsertionEffect(false);285              }286            }287288            if (enableSchedulingProfiler) {289              if ((flags & HookPassive) !== NoHookEffect) {290                markComponentPassiveEffectUnmountStopped();291              } else if ((flags & HookLayout) !== NoHookEffect) {292                markComponentLayoutEffectUnmountStopped();293              }294            }295          }296        }297        effect = effect.next;298      } while (effect !== firstEffect);299    }300  } catch (error) {301    captureCommitPhaseError(finishedWork, finishedWork.return, error);302  }303}304305export function commitHookPassiveMountEffects(306  finishedWork: Fiber,307  hookFlags: HookFlags,308) {309  if (shouldProfile(finishedWork)) {310    startEffectTimer();311    commitHookEffectListMount(hookFlags, finishedWork);312    recordEffectDuration(finishedWork);313  } else {314    commitHookEffectListMount(hookFlags, finishedWork);315  }316}317318export function commitHookPassiveUnmountEffects(319  finishedWork: Fiber,320  nearestMountedAncestor: null | Fiber,321  hookFlags: HookFlags,322) {323  if (shouldProfile(finishedWork)) {324    startEffectTimer();325    commitHookEffectListUnmount(326      hookFlags,327      finishedWork,328      nearestMountedAncestor,329    );330    recordEffectDuration(finishedWork);331  } else {332    commitHookEffectListUnmount(333      hookFlags,334      finishedWork,335      nearestMountedAncestor,336    );337  }338}339340export function commitClassLayoutLifecycles(341  finishedWork: Fiber,342  current: Fiber | null,343) {344  const instance = finishedWork.stateNode;345  if (current === null) {346    // We could update instance props and state here,347    // but instead we rely on them being set during last render.348    // TODO: revisit this when we implement resuming.349    if (__DEV__) {350      if (351        !finishedWork.type.defaultProps &&352        !('ref' in finishedWork.memoizedProps) &&353        !didWarnAboutReassigningProps354      ) {355        if (instance.props !== finishedWork.memoizedProps) {356          console.error(357            'Expected %s props to match memoized props before ' +358              'componentDidMount. ' +359              'This might either be because of a bug in React, or because ' +360              'a component reassigns its own `this.props`. ' +361              'Please file an issue.',362            getComponentNameFromFiber(finishedWork) || 'instance',363          );364        }365        if (instance.state !== finishedWork.memoizedState) {366          console.error(367            'Expected %s state to match memoized state before ' +368              'componentDidMount. ' +369              'This might either be because of a bug in React, or because ' +370              'a component reassigns its own `this.state`. ' +371              'Please file an issue.',372            getComponentNameFromFiber(finishedWork) || 'instance',373          );374        }375      }376    }377    if (shouldProfile(finishedWork)) {378      startEffectTimer();379      if (__DEV__) {380        runWithFiberInDEV(381          finishedWork,382          callComponentDidMountInDEV,383          finishedWork,384          instance,385        );386      } else {387        try {388          instance.componentDidMount();389        } catch (error) {390          captureCommitPhaseError(finishedWork, finishedWork.return, error);391        }392      }393      recordEffectDuration(finishedWork);394    } else {395      if (__DEV__) {396        runWithFiberInDEV(397          finishedWork,398          callComponentDidMountInDEV,399          finishedWork,400          instance,401        );402      } else {403        try {404          instance.componentDidMount();405        } catch (error) {406          captureCommitPhaseError(finishedWork, finishedWork.return, error);407        }408      }409    }410  } else {411    const prevProps = resolveClassComponentProps(412      finishedWork.type,413      current.memoizedProps,414    );415    const prevState = current.memoizedState;416    // We could update instance props and state here,417    // but instead we rely on them being set during last render.418    // TODO: revisit this when we implement resuming.419    if (__DEV__) {420      if (421        !finishedWork.type.defaultProps &&422        !('ref' in finishedWork.memoizedProps) &&423        !didWarnAboutReassigningProps424      ) {425        if (instance.props !== finishedWork.memoizedProps) {426          console.error(427            'Expected %s props to match memoized props before ' +428              'componentDidUpdate. ' +429              'This might either be because of a bug in React, or because ' +430              'a component reassigns its own `this.props`. ' +431              'Please file an issue.',432            getComponentNameFromFiber(finishedWork) || 'instance',433          );434        }435        if (instance.state !== finishedWork.memoizedState) {436          console.error(437            'Expected %s state to match memoized state before ' +438              'componentDidUpdate. ' +439              'This might either be because of a bug in React, or because ' +440              'a component reassigns its own `this.state`. ' +441              'Please file an issue.',442            getComponentNameFromFiber(finishedWork) || 'instance',443          );444        }445      }446    }447    if (shouldProfile(finishedWork)) {448      startEffectTimer();449      if (__DEV__) {450        runWithFiberInDEV(451          finishedWork,452          callComponentDidUpdateInDEV,453          finishedWork,454          instance,455          prevProps,456          prevState,457          instance.__reactInternalSnapshotBeforeUpdate,458        );459      } else {460        try {461          instance.componentDidUpdate(462            prevProps,463            prevState,464            instance.__reactInternalSnapshotBeforeUpdate,465          );466        } catch (error) {467          captureCommitPhaseError(finishedWork, finishedWork.return, error);468        }469      }470      recordEffectDuration(finishedWork);471    } else {472      if (__DEV__) {473        runWithFiberInDEV(474          finishedWork,475          callComponentDidUpdateInDEV,476          finishedWork,477          instance,478          prevProps,479          prevState,480          instance.__reactInternalSnapshotBeforeUpdate,481        );482      } else {483        try {484          instance.componentDidUpdate(485            prevProps,486            prevState,487            instance.__reactInternalSnapshotBeforeUpdate,488          );489        } catch (error) {490          captureCommitPhaseError(finishedWork, finishedWork.return, error);491        }492      }493    }494  }495}496497export function commitClassDidMount(finishedWork: Fiber) {498  // TODO: Check for LayoutStatic flag499  const instance = finishedWork.stateNode;500  if (typeof instance.componentDidMount === 'function') {501    if (__DEV__) {502      runWithFiberInDEV(503        finishedWork,504        callComponentDidMountInDEV,505        finishedWork,506        instance,507      );508    } else {509      try {510        instance.componentDidMount();511      } catch (error) {512        captureCommitPhaseError(finishedWork, finishedWork.return, error);513      }514    }515  }516}517518export function commitClassCallbacks(finishedWork: Fiber) {519  // TODO: I think this is now always non-null by the time it reaches the520  // commit phase. Consider removing the type check.521  const updateQueue: UpdateQueue<mixed> | null =522    finishedWork.updateQueue as any;523  if (updateQueue !== null) {524    const instance = finishedWork.stateNode;525    if (__DEV__) {526      if (527        !finishedWork.type.defaultProps &&528        !('ref' in finishedWork.memoizedProps) &&529        !didWarnAboutReassigningProps530      ) {531        if (instance.props !== finishedWork.memoizedProps) {532          console.error(533            'Expected %s props to match memoized props before ' +534              'processing the update queue. ' +535              'This might either be because of a bug in React, or because ' +536              'a component reassigns its own `this.props`. ' +537              'Please file an issue.',538            getComponentNameFromFiber(finishedWork) || 'instance',539          );540        }541        if (instance.state !== finishedWork.memoizedState) {542          console.error(543            'Expected %s state to match memoized state before ' +544              'processing the update queue. ' +545              'This might either be because of a bug in React, or because ' +546              'a component reassigns its own `this.state`. ' +547              'Please file an issue.',548            getComponentNameFromFiber(finishedWork) || 'instance',549          );550        }551      }552    }553    // We could update instance props and state here,554    // but instead we rely on them being set during last render.555    // TODO: revisit this when we implement resuming.556    try {557      if (__DEV__) {558        runWithFiberInDEV(finishedWork, commitCallbacks, updateQueue, instance);559      } else {560        commitCallbacks(updateQueue, instance);561      }562    } catch (error) {563      captureCommitPhaseError(finishedWork, finishedWork.return, error);564    }565  }566}567568export function commitClassHiddenCallbacks(finishedWork: Fiber) {569  // Commit any callbacks that would have fired while the component570  // was hidden.571  const updateQueue: UpdateQueue<mixed> | null =572    finishedWork.updateQueue as any;573  if (updateQueue !== null) {574    const instance = finishedWork.stateNode;575    try {576      if (__DEV__) {577        runWithFiberInDEV(578          finishedWork,579          commitHiddenCallbacks,580          updateQueue,581          instance,582        );583      } else {584        commitHiddenCallbacks(updateQueue, instance);585      }586    } catch (error) {587      captureCommitPhaseError(finishedWork, finishedWork.return, error);588    }589  }590}591592export function commitRootCallbacks(finishedWork: Fiber) {593  // TODO: I think this is now always non-null by the time it reaches the594  // commit phase. Consider removing the type check.595  const updateQueue: UpdateQueue<mixed> | null =596    finishedWork.updateQueue as any;597  if (updateQueue !== null) {598    let instance = null;599    if (finishedWork.child !== null) {600      switch (finishedWork.child.tag) {601        case HostSingleton:602        case HostComponent:603          instance = getPublicInstance(finishedWork.child.stateNode);604          break;605        case ClassComponent:606          instance = finishedWork.child.stateNode;607          break;608      }609    }610    try {611      if (__DEV__) {612        runWithFiberInDEV(finishedWork, commitCallbacks, updateQueue, instance);613      } else {614        commitCallbacks(updateQueue, instance);615      }616    } catch (error) {617      captureCommitPhaseError(finishedWork, finishedWork.return, error);618    }619  }620}621622let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;623if (__DEV__) {624  didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();625}626627function callGetSnapshotBeforeUpdates(628  instance: any,629  prevProps: any,630  prevState: any,631) {632  return instance.getSnapshotBeforeUpdate(prevProps, prevState);633}634635export function commitClassSnapshot(finishedWork: Fiber, current: Fiber) {636  const prevProps = current.memoizedProps;637  const prevState = current.memoizedState;638  const instance = finishedWork.stateNode;639  // We could update instance props and state here,640  // but instead we rely on them being set during last render.641  // TODO: revisit this when we implement resuming.642  if (__DEV__) {643    if (644      !finishedWork.type.defaultProps &&645      !('ref' in finishedWork.memoizedProps) &&646      !didWarnAboutReassigningProps647    ) {648      if (instance.props !== finishedWork.memoizedProps) {649        console.error(650          'Expected %s props to match memoized props before ' +651            'getSnapshotBeforeUpdate. ' +652            'This might either be because of a bug in React, or because ' +653            'a component reassigns its own `this.props`. ' +654            'Please file an issue.',655          getComponentNameFromFiber(finishedWork) || 'instance',656        );657      }658      if (instance.state !== finishedWork.memoizedState) {659        console.error(660          'Expected %s state to match memoized state before ' +661            'getSnapshotBeforeUpdate. ' +662            'This might either be because of a bug in React, or because ' +663            'a component reassigns its own `this.state`. ' +664            'Please file an issue.',665          getComponentNameFromFiber(finishedWork) || 'instance',666        );667      }668    }669  }670  try {671    const resolvedPrevProps = resolveClassComponentProps(672      finishedWork.type,673      prevProps,674    );675    let snapshot;676    if (__DEV__) {677      snapshot = runWithFiberInDEV(678        finishedWork,679        callGetSnapshotBeforeUpdates,680        instance,681        resolvedPrevProps,682        prevState,683      );684      const didWarnSet =685        didWarnAboutUndefinedSnapshotBeforeUpdate as any as Set<mixed>;686      if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {687        didWarnSet.add(finishedWork.type);688        runWithFiberInDEV(finishedWork, () => {689          console.error(690            '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +691              'must be returned. You have returned undefined.',692            getComponentNameFromFiber(finishedWork),693          );694        });695      }696    } else {697      snapshot = callGetSnapshotBeforeUpdates(698        instance,699        resolvedPrevProps,700        prevState,701      );702    }703    instance.__reactInternalSnapshotBeforeUpdate = snapshot;704  } catch (error) {705    captureCommitPhaseError(finishedWork, finishedWork.return, error);706  }707}708709// Capture errors so they don't interrupt unmounting.710export function safelyCallComponentWillUnmount(711  current: Fiber,712  nearestMountedAncestor: Fiber | null,713  instance: any,714) {715  instance.props = resolveClassComponentProps(716    current.type,717    current.memoizedProps,718  );719  instance.state = current.memoizedState;720  if (shouldProfile(current)) {721    startEffectTimer();722    if (__DEV__) {723      runWithFiberInDEV(724        current,725        callComponentWillUnmountInDEV,726        current,727        nearestMountedAncestor,728        instance,729      );730    } else {731      try {732        instance.componentWillUnmount();733      } catch (error) {734        captureCommitPhaseError(current, nearestMountedAncestor, error);735      }736    }737    recordEffectDuration(current);738  } else {739    if (__DEV__) {740      runWithFiberInDEV(741        current,742        callComponentWillUnmountInDEV,743        current,744        nearestMountedAncestor,745        instance,746      );747    } else {748      try {749        instance.componentWillUnmount();750      } catch (error) {751        captureCommitPhaseError(current, nearestMountedAncestor, error);752      }753    }754  }755}756757function commitAttachRef(finishedWork: Fiber) {758  const ref = finishedWork.ref;759  if (ref !== null) {760    let instanceToUse;761    switch (finishedWork.tag) {762      case HostHoistable:763      case HostSingleton:764      case HostComponent:765        instanceToUse = getPublicInstance(finishedWork.stateNode);766        break;767      case ViewTransitionComponent: {768        if (enableViewTransition) {769          const instance: ViewTransitionState = finishedWork.stateNode;770          const props: ViewTransitionProps = finishedWork.memoizedProps;771          const name = getViewTransitionName(props, instance);772          if (instance.ref === null || instance.ref.name !== name) {773            instance.ref = createViewTransitionInstance(name);774          }775          instanceToUse = instance.ref;776          break;777        }778        instanceToUse = finishedWork.stateNode;779        break;780      }781      case Fragment:782        if (enableFragmentRefs) {783          const instance: null | FragmentInstanceType = finishedWork.stateNode;784          if (instance === null) {785            finishedWork.stateNode = createFragmentInstance(finishedWork);786          }787          instanceToUse = finishedWork.stateNode;788          break;789        }790      // Fallthrough791      default:792        instanceToUse = finishedWork.stateNode;793    }794    if (typeof ref === 'function') {795      if (shouldProfile(finishedWork)) {796        try {797          startEffectTimer();798          finishedWork.refCleanup = ref(instanceToUse);799        } finally {800          recordEffectDuration(finishedWork);801        }802      } else {803        finishedWork.refCleanup = ref(instanceToUse);804      }805    } else {806      if (__DEV__) {807        // TODO: We should move these warnings to happen during the render808        // phase (markRef).809        if (typeof ref === 'string') {810          console.error('String refs are no longer supported.');811        } else if (!ref.hasOwnProperty('current')) {812          console.error(813            'Unexpected ref object provided for %s. ' +814              'Use either a ref-setter function or React.createRef().',815            getComponentNameFromFiber(finishedWork),816          );817        }818      }819820      // $FlowFixMe[incompatible-use] unable to narrow type to the non-function case821      ref.current = instanceToUse;822    }823  }824}825826// Capture errors so they don't interrupt mounting.827export function safelyAttachRef(828  current: Fiber,829  nearestMountedAncestor: Fiber | null,830) {831  try {832    if (__DEV__) {833      runWithFiberInDEV(current, commitAttachRef, current);834    } else {835      commitAttachRef(current);836    }837  } catch (error) {838    captureCommitPhaseError(current, nearestMountedAncestor, error);839  }840}841842export function safelyDetachRef(843  current: Fiber,844  nearestMountedAncestor: Fiber | null,845) {846  const ref = current.ref;847  const refCleanup = current.refCleanup;848849  if (ref !== null) {850    if (typeof refCleanup === 'function') {851      try {852        if (shouldProfile(current)) {853          try {854            startEffectTimer();855            if (__DEV__) {856              runWithFiberInDEV(current, refCleanup);857            } else {858              refCleanup();859            }860          } finally {861            recordEffectDuration(current);862          }863        } else {864          if (__DEV__) {865            runWithFiberInDEV(current, refCleanup);866          } else {867            refCleanup();868          }869        }870      } catch (error) {871        captureCommitPhaseError(current, nearestMountedAncestor, error);872      } finally {873        // `refCleanup` has been called. Nullify all references to it to prevent double invocation.874        current.refCleanup = null;875        const finishedWork = current.alternate;876        if (finishedWork != null) {877          finishedWork.refCleanup = null;878        }879      }880    } else if (typeof ref === 'function') {881      try {882        if (shouldProfile(current)) {883          try {884            startEffectTimer();885            if (__DEV__) {886              runWithFiberInDEV(current, ref, null) as void;887            } else {888              ref(null);889            }890          } finally {891            recordEffectDuration(current);892          }893        } else {894          if (__DEV__) {895            runWithFiberInDEV(current, ref, null) as void;896          } else {897            ref(null);898          }899        }900      } catch (error) {901        captureCommitPhaseError(current, nearestMountedAncestor, error);902      }903    } else {904      // $FlowFixMe[incompatible-use] unable to narrow type to RefObject905      ref.current = null;906    }907  }908}909910function safelyCallDestroy(911  current: Fiber,912  nearestMountedAncestor: Fiber | null,913  destroy: (() => void) | (({...}) => void),914  resource?: {...} | void | null,915) {916  // $FlowFixMe[extra-arg] @poteto this is safe either way because the extra arg is ignored if it's not a CRUD effect917  const destroy_ = resource == null ? destroy : destroy.bind(null, resource);918  if (__DEV__) {919    runWithFiberInDEV(920      current,921      callDestroyInDEV,922      current,923      nearestMountedAncestor,924      destroy_,925    );926  } else {927    try {928      // $FlowFixMe[incompatible-type](incompatible-call) Already bound to resource929      destroy_();930    } catch (error) {931      captureCommitPhaseError(current, nearestMountedAncestor, error);932    }933  }934}935936function commitProfiler(937  finishedWork: Fiber,938  current: Fiber | null,939  commitStartTime: number,940  effectDuration: number,941) {942  const {id, onCommit, onRender} = finishedWork.memoizedProps as ProfilerProps;943944  let phase: ProfilerPhase = current === null ? 'mount' : 'update';945  if (enableProfilerNestedUpdatePhase) {946    if (isCurrentUpdateNested()) {947      phase = 'nested-update';948    }949  }950951  if (typeof onRender === 'function') {952    onRender(953      id,954      phase,955      // $FlowFixMe[incompatible-type]: This should be always a number in profiling mode956      finishedWork.actualDuration,957      // $FlowFixMe[incompatible-type]: This should be always a number in profiling mode958      finishedWork.treeBaseDuration,959      // $FlowFixMe[incompatible-type]: This should be always a number in profiling mode960      finishedWork.actualStartTime,961      commitStartTime,962    );963  }964965  if (enableProfilerCommitHooks) {966    if (typeof onCommit === 'function') {967      onCommit(id, phase, effectDuration, commitStartTime);968    }969  }970}971972export function commitProfilerUpdate(973  finishedWork: Fiber,974  current: Fiber | null,975  commitStartTime: number,976  effectDuration: number,977) {978  if (enableProfilerTimer) {979    try {980      if (__DEV__) {981        runWithFiberInDEV(982          finishedWork,983          commitProfiler,984          finishedWork,985          current,986          commitStartTime,987          effectDuration,988        );989      } else {990        commitProfiler(finishedWork, current, commitStartTime, effectDuration);991      }992    } catch (error) {993      captureCommitPhaseError(finishedWork, finishedWork.return, error);994    }995  }996}997998function commitProfilerPostCommitImpl(999  finishedWork: Fiber,1000  current: Fiber | null,1001  commitStartTime: number,1002  passiveEffectDuration: number,1003): void {1004  const {id, onPostCommit} = finishedWork.memoizedProps;10051006  let phase = current === null ? 'mount' : 'update';1007  if (enableProfilerNestedUpdatePhase) {1008    if (isCurrentUpdateNested()) {1009      phase = 'nested-update';1010    }1011  }10121013  if (typeof onPostCommit === 'function') {1014    onPostCommit(id, phase, passiveEffectDuration, commitStartTime);1015  }1016}10171018export function commitProfilerPostCommit(1019  finishedWork: Fiber,1020  current: Fiber | null,1021  commitStartTime: number,1022  passiveEffectDuration: number,1023) {1024  try {1025    if (__DEV__) {1026      runWithFiberInDEV(1027        finishedWork,1028        commitProfilerPostCommitImpl,1029        finishedWork,1030        current,1031        commitStartTime,1032        passiveEffectDuration,1033      );1034    } else {1035      commitProfilerPostCommitImpl(1036        finishedWork,1037        current,1038        commitStartTime,1039        passiveEffectDuration,1040      );1041    }1042  } catch (error) {1043    captureCommitPhaseError(finishedWork, finishedWork.return, error);1044  }1045}

Code quality findings 76

Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(current.mode & ProfileMode) !== NoMode
Ensure try blocks have corresponding catch or finally blocks
info correctness try-without-catch
try {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lastEffect !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((effect.tag & flags) === flags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookPassive) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if ((flags & HookLayout) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookInsertion) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookInsertion) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookPassive) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if ((flags & HookLayout) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (destroy !== undefined && typeof destroy !== 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (destroy !== undefined && typeof destroy !== 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((effect.tag & HookLayout) !== NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if ((effect.tag & HookInsertion) !== NoFlags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (destroy === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (typeof destroy.then === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
} else if (typeof destroy.then === 'function') {
Ensure all async functions handle errors properly
info correctness async-without-catch
'Instead, write the async function inside your effect ' +
Ensure all async functions handle errors properly
info correctness async-without-catch
' async function fetchData() {\n' +
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} while (effect !== firstEffect);
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lastEffect !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((effect.tag & flags) === flags) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (destroy !== undefined) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookPassive) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if ((flags & HookLayout) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookInsertion) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookInsertion) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((flags & HookPassive) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if ((flags & HookLayout) !== NoHookEffect) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} while (effect !== firstEffect);
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 (instance.props !== finishedWork.memoizedProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.state !== finishedWork.memoizedState) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.props !== finishedWork.memoizedProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.state !== finishedWork.memoizedState) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof instance.componentDidMount === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof instance.componentDidMount === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (updateQueue !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.props !== finishedWork.memoizedProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.state !== finishedWork.memoizedState) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (updateQueue !== null) {
Ensure try blocks have corresponding catch or finally blocks
info correctness try-without-catch
try {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (updateQueue !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (finishedWork.child !== null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (finishedWork.child.tag) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.props !== finishedWork.memoizedProps) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.state !== finishedWork.memoizedState) {
Ensure try blocks have corresponding catch or finally blocks
info correctness try-without-catch
try {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (ref !== null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (finishedWork.tag) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance.ref === null || instance.ref.name !== name) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (instance === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof ref === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof ref === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof ref === 'string') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof ref === 'string') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (ref !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof refCleanup === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof refCleanup === 'function') {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (finishedWork != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (typeof ref === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
} else if (typeof ref === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
const destroy_ = resource == null ? destroy : destroy.bind(null, resource);
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
let phase: ProfilerPhase = current === null ? 'mount' : 'update';
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof onRender === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof onRender === 'function') {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof onCommit === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof onCommit === 'function') {
Ensure try blocks have corresponding catch or finally blocks
info correctness try-without-catch
try {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
let phase = current === null ? 'mount' : 'update';
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof onPostCommit === 'function') {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof onPostCommit === 'function') {
Ensure try blocks have corresponding catch or finally blocks
info correctness try-without-catch
try {

Get this view in your editor

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