text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   */
  
  import type {
    MutableSource,
    MutableSourceGetSnapshotFn,
    MutableSourceSubscribeFn,
    ReactContext,
  } from 'shared/ReactTypes';
  import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes';
  import type {Lanes, Lane} from './ReactFiberLane.old';
  import type {HookFlags} from './ReactHookEffectTags';
  import type {ReactPriorityLevel} from './ReactInternalTypes';
  import type {FiberRoot} from './ReactInternalTypes';
  import type {OpaqueIDType} from './ReactFiberHostConfig';
  import type {Cache} from './ReactFiberCacheComponent.old';
  
  import ReactSharedInternals from 'shared/ReactSharedInternals';
  import {
    enableDebugTracing,
    enableSchedulingProfiler,
    enableNewReconciler,
    enableCache,
    enableUseRefAccessWarning,
    enableStrictEffects,
    enableLazyContextPropagation,
  } from 'shared/ReactFeatureFlags';
  
  import {
    NoMode,
    BlockingMode,
    DebugTracingMode,
    StrictEffectsMode,
  } from './ReactTypeOfMode';
  import {
    NoLane,
    NoLanes,
    InputContinuousLanePriority,
    isSubsetOfLanes,
    mergeLanes,
    removeLanes,
    intersectLanes,
    isTransitionLane,
    markRootEntangled,
    markRootMutableRead,
    getCurrentUpdateLanePriority,
    setCurrentUpdateLanePriority,
    higherLanePriority,
    DefaultLanePriority,
  } from './ReactFiberLane.old';
  import {readContext, checkIfContextChanged} from './ReactFiberNewContext.old';
  import {HostRoot, CacheComponent} from './ReactWorkTags';
  import {
    Update as UpdateEffect,
    Passive as PassiveEffect,
    PassiveStatic as PassiveStaticEffect,
    MountLayoutDev as MountLayoutDevEffect,
    MountPassiveDev as MountPassiveDevEffect,
  } from './ReactFiberFlags';
  import {
    HasEffect as HookHasEffect,
    Layout as HookLayout,
    Passive as HookPassive,
  } from './ReactHookEffectTags';
  import {
    getWorkInProgressRoot,
    scheduleUpdateOnFiber,
    requestUpdateLane,
    requestEventTime,
    warnIfNotCurrentlyActingEffectsInDEV,
    warnIfNotCurrentlyActingUpdatesInDev,
    warnIfNotScopedWithMatchingAct,
    markSkippedUpdateLanes,
    isInterleavedUpdate,
  } from './ReactFiberWorkLoop.old';
  
  import invariant from 'shared/invariant';
  import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
  import is from 'shared/objectIs';
  import {
    markWorkInProgressReceivedUpdate,
    checkIfWorkInProgressReceivedUpdate,
  } from './ReactFiberBeginWork.old';
  import {getIsHydrating} from './ReactFiberHydrationContext.old';
  import {
    makeClientId,
    makeClientIdInDEV,
    makeOpaqueHydratingObject,
  } from './ReactFiberHostConfig';
  import {
    getWorkInProgressVersion,
    markSourceAsDirty,
    setWorkInProgressVersion,
    warnAboutMultipleRenderersDEV,
  } from './ReactMutableSource.old';
  import {getIsRendering} from './ReactCurrentFiber';
  import {logStateUpdateScheduled} from './DebugTracing';
  import {markStateUpdateScheduled} from './SchedulingProfiler';
  import {CacheContext} from './ReactFiberCacheComponent.old';
  import {
    createUpdate,
    enqueueUpdate,
    entangleTransitions,
  } from './ReactUpdateQueue.old';
  import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.old';
  
  const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
  
  type Update<S, A> = {|
    lane: Lane,
    action: A,
    eagerReducer: ((S, A) => S) | null,
    eagerState: S | null,
    next: Update<S, A>,
    priority?: ReactPriorityLevel,
  |};
  
  export type UpdateQueue<S, A> = {|
    pending: Update<S, A> | null,
    interleaved: Update<S, A> | null,
    lanes: Lanes,
    dispatch: (A => mixed) | null,
    lastRenderedReducer: ((S, A) => S) | null,
    lastRenderedState: S | null,
  |};
  
  let didWarnAboutMismatchedHooksForComponent;
  let didWarnAboutUseOpaqueIdentifier;
  if (__DEV__) {
    didWarnAboutUseOpaqueIdentifier = {};
    didWarnAboutMismatchedHooksForComponent = new Set();
  }
  
  export type Hook = {|
    memoizedState: any,
    baseState: any,
    baseQueue: Update<any, any> | null,
    queue: UpdateQueue<any, any> | null,
    next: Hook | null,
  |};
  
  export type Effect = {|
    tag: HookFlags,
    create: () => (() => void) | void,
    destroy: (() => void) | void,
    deps: Array<mixed> | null,
    next: Effect,
  |};
  
  export type FunctionComponentUpdateQueue = {|lastEffect: Effect | null|};
  
  type BasicStateAction<S> = (S => S) | S;
  
  type Dispatch<A> = A => void;
  
  // These are set right before calling the component.
  let renderLanes: Lanes = NoLanes;
  // The work-in-progress fiber. I've named it differently to distinguish it from
  // the work-in-progress hook.
  let currentlyRenderingFiber: Fiber = (null: any);
  
  // Hooks are stored as a linked list on the fiber's memoizedState field. The
  // current hook list is the list that belongs to the current fiber. The
  // work-in-progress hook list is a new list that will be added to the
  // work-in-progress fiber.
  let currentHook: Hook | null = null;
  let workInProgressHook: Hook | null = null;
  
  // Whether an update was scheduled at any point during the render phase. This
  // does not get reset if we do another render pass; only when we're completely
  // finished evaluating this component. This is an optimization so we know
  // whether we need to clear render phase updates after a throw.
  let didScheduleRenderPhaseUpdate: boolean = false;
  // Where an update was scheduled only during the current render pass. This
  // gets reset after each attempt.
  // TODO: Maybe there's some way to consolidate this with
  // `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`.
  let didScheduleRenderPhaseUpdateDuringThisPass: boolean = false;
  
  const RE_RENDER_LIMIT = 25;
  
  // In DEV, this is the name of the currently executing primitive hook
  let currentHookNameInDev: ?HookType = null;
  
  // In DEV, this list ensures that hooks are called in the same order between renders.
  // The list stores the order of hooks used during the initial render (mount).
  // Subsequent renders (updates) reference this list.
  let hookTypesDev: Array<HookType> | null = null;
  let hookTypesUpdateIndexDev: number = -1;
  
  // In DEV, this tracks whether currently rendering component needs to ignore
  // the dependencies for Hooks that need them (e.g. useEffect or useMemo).
  // When true, such Hooks will always be "remounted". Only used during hot reload.
  let ignorePreviousDependencies: boolean = false;
  
  function mountHookTypesDev() {
    if (__DEV__) {
      const hookName = ((currentHookNameInDev: any): HookType);
  
      if (hookTypesDev === null) {
        hookTypesDev = [hookName];
      } else {
        hookTypesDev.push(hookName);
      }
    }
  }
  
  function updateHookTypesDev() {
    if (__DEV__) {
      const hookName = ((currentHookNameInDev: any): HookType);
  
      if (hookTypesDev !== null) {
        hookTypesUpdateIndexDev++;
        if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
          warnOnHookMismatchInDev(hookName);
        }
      }
    }
  }
  
  function checkDepsAreArrayDev(deps: mixed) {
    if (__DEV__) {
      if (deps !== undefined && deps !== null && !Array.isArray(deps)) {
        // Verify deps, but only on mount to avoid extra checks.
        // It's unlikely their type would change as usually you define them inline.
        console.error(
          '%s received a final argument that is not an array (instead, received `%s`). When ' +
            'specified, the final argument must be an array.',
          currentHookNameInDev,
          typeof deps,
        );
      }
    }
  }
  
  function warnOnHookMismatchInDev(currentHookName: HookType) {
    if (__DEV__) {
      const componentName = getComponentNameFromFiber(currentlyRenderingFiber);
      if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
        didWarnAboutMismatchedHooksForComponent.add(componentName);
  
        if (hookTypesDev !== null) {
          let table = '';
  
          const secondColumnStart = 30;
  
          for (let i = 0; i <= ((hookTypesUpdateIndexDev: any): number); i++) {
            const oldHookName = hookTypesDev[i];
            const newHookName =
              i === ((hookTypesUpdateIndexDev: any): number)
                ? currentHookName
                : oldHookName;
  
            let row = `${i + 1}. ${oldHookName}`;
  
            // Extra space so second column lines up
            // lol @ IE not supporting String#repeat
            while (row.length < secondColumnStart) {
              row += ' ';
            }
  
            row += newHookName + '\n';
  
            table += row;
          }
  
          console.error(
            'React has detected a change in the order of Hooks called by %s. ' +
              'This will lead to bugs and errors if not fixed. ' +
              'For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' +
              '   Previous render            Next render\n' +
              '   ------------------------------------------------------\n' +
              '%s' +
              '   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n',
            componentName,
            table,
          );
        }
      }
    }
  }
  
  function throwInvalidHookError() {
    invariant(
      false,
      'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
        ' one of the following reasons:\n' +
        '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
        '2. You might be breaking the Rules of Hooks\n' +
        '3. You might have more than one copy of React in the same app\n' +
        'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    );
  }
  
  function areHookInputsEqual(
    nextDeps: Array<mixed>,
    prevDeps: Array<mixed> | null,
  ) {
    if (__DEV__) {
      if (ignorePreviousDependencies) {
        // Only true when this component is being hot reloaded.
        return false;
      }
    }
  
    if (prevDeps === null) {
      if (__DEV__) {
        console.error(
          '%s received a final argument during this render, but not during ' +
            'the previous render. Even though the final argument is optional, ' +
            'its type cannot change between renders.',
          currentHookNameInDev,
        );
      }
      return false;
    }
  
    if (__DEV__) {
      // Don't bother comparing lengths in prod because these arrays should be
      // passed inline.
      if (nextDeps.length !== prevDeps.length) {
        console.error(
          'The final argument passed to %s changed size between renders. The ' +
            'order and size of this array must remain constant.\n\n' +
            'Previous: %s\n' +
            'Incoming: %s',
          currentHookNameInDev,
          `[${prevDeps.join(', ')}]`,
          `[${nextDeps.join(', ')}]`,
        );
      }
    }
    for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
      if (is(nextDeps[i], prevDeps[i])) {
        continue;
      }
      return false;
    }
    return true;
  }
  
  export function renderWithHooks<Props, SecondArg>(
    current: Fiber | null, // 对应的 fiber current树
    workInProgress: Fiber,  // 对应的 fiber workInProgress树
    Component: (p: Props, arg: SecondArg) => any, // 包含hooks的function组件
    props: Props,
    secondArg: SecondArg,
    nextRenderLanes: Lanes,
  ): any {
    renderLanes = nextRenderLanes;
    currentlyRenderingFiber = workInProgress; // 用于后面dispatchAction获取当前fiber节点
  
    if (__DEV__) {
      hookTypesDev =
        current !== null
          ? ((current._debugHookTypes: any): Array<HookType>)
          : null;
      hookTypesUpdateIndexDev = -1;
      // Used for hot reloading:
      ignorePreviousDependencies =
        current !== null && current.type !== workInProgress.type;
    }
  
    workInProgress.memoizedState = null;
    workInProgress.updateQueue = null;
    workInProgress.lanes = NoLanes;
  
    // The following should have already been reset
    // currentHook = null;
    // workInProgressHook = null;
  
    // didScheduleRenderPhaseUpdate = false;
  
    // TODO Warn if no hooks are used at all during mount, then some are used during update.
    // Currently we will identify the update render as a mount because memoizedState === null.
    // This is tricky because it's valid for certain types of components (e.g. React.lazy)
  
    // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
    // Non-stateful hooks (e.g. context) don't get added to memoizedState,
    // so memoizedState would be null during updates and mounts.
    if (__DEV__) {
      if (current !== null && current.memoizedState !== null) {
        ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
      } else if (hookTypesDev !== null) {
        // This dispatcher handles an edge case where a component is updating,
        // but no stateful hooks have been used.
        // We want to match the production code behavior (which will use HooksDispatcherOnMount),
        // but with the extra DEV validation to ensure hooks ordering hasn't changed.
        // This dispatcher does that.
        ReactCurrentDispatcher.current = HooksDispatcherOnMountWithHookTypesInDEV;
      } else {
        ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
      }
    } else {
      ReactCurrentDispatcher.current =
        current === null || current.memoizedState === null
          ? HooksDispatcherOnMount
          : HooksDispatcherOnUpdate;
    }
  
    let children = Component(props, secondArg);
  
    // Check if there was a render phase update
    if (didScheduleRenderPhaseUpdateDuringThisPass) {
      // Keep rendering in a loop for as long as render phase updates continue to
      // be scheduled. Use a counter to prevent infinite loops.
      let numberOfReRenders: number = 0;
      do {
        didScheduleRenderPhaseUpdateDuringThisPass = false;
        invariant(
          numberOfReRenders < RE_RENDER_LIMIT,
          'Too many re-renders. React limits the number of renders to prevent ' +
            'an infinite loop.',
        );
  
        numberOfReRenders += 1;
        if (__DEV__) {
          // Even when hot reloading, allow dependencies to stabilize
          // after first render to prevent infinite render phase updates.
          ignorePreviousDependencies = false;
        }
  
        // Start over from the beginning of the list
        currentHook = null;
        workInProgressHook = null;
  
        workInProgress.updateQueue = null;
  
        if (__DEV__) {
          // Also validate hook order for cascading updates.
          hookTypesUpdateIndexDev = -1;
        }
  
        ReactCurrentDispatcher.current = __DEV__
          ? HooksDispatcherOnRerenderInDEV
          : HooksDispatcherOnRerender;
  
        children = Component(props, secondArg);
      } while (didScheduleRenderPhaseUpdateDuringThisPass);
    }
  
    // We can assume the previous dispatcher is always this one, since we set it
    // at the beginning of the render phase and there's no re-entrancy.
    ReactCurrentDispatcher.current = ContextOnlyDispatcher;
  
    if (__DEV__) {
      workInProgress._debugHookTypes = hookTypesDev;
    }
  
    // This check uses currentHook so that it works the same in DEV and prod bundles.
    // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
    const didRenderTooFewHooks =
      currentHook !== null && currentHook.next !== null;
  
    renderLanes = NoLanes;
    currentlyRenderingFiber = (null: any);
  
    currentHook = null;
    workInProgressHook = null;
  
    if (__DEV__) {
      currentHookNameInDev = null;
      hookTypesDev = null;
      hookTypesUpdateIndexDev = -1;
  
      // Confirm that a static flag was not added or removed since the last
      // render. If this fires, it suggests that we incorrectly reset the static
      // flags in some other part of the codebase. This has happened before, for
      // example, in the SuspenseList implementation.
      if (
        current !== null &&
        (current.flags & PassiveStaticEffect) !==
          (workInProgress.flags & PassiveStaticEffect)
      ) {
        console.error(
          'Internal React error: Expected static flag was missing. Please ' +
            'notify the React team.',
        );
      }
    }
  
    didScheduleRenderPhaseUpdate = false;
  
    invariant(
      !didRenderTooFewHooks,
      'Rendered fewer hooks than expected. This may be caused by an accidental ' +
        'early return statement.',
    );
  
    if (enableLazyContextPropagation) {
      if (current !== null) {
        if (!checkIfWorkInProgressReceivedUpdate()) {
          // If there were no changes to props or state, we need to check if there
          // was a context change. We didn't already do this because there's no
          // 1:1 correspondence between dependencies and hooks. Although, because
          // there almost always is in the common case (`readContext` is an
          // internal API), we could compare in there. OTOH, we only hit this case
          // if everything else bails out, so on the whole it might be better to
          // keep the comparison out of the common path.
          const currentDependencies = current.dependencies;
          if (
            currentDependencies !== null &&
            checkIfContextChanged(currentDependencies)
          ) {
            markWorkInProgressReceivedUpdate();
          }
        }
      }
    }
  
    return children;
  }
  
  export function bailoutHooks(
    current: Fiber,
    workInProgress: Fiber,
    lanes: Lanes,
  ) {
    workInProgress.updateQueue = current.updateQueue;
    // TODO: Don't need to reset the flags here, because they're reset in the
    // complete phase (bubbleProperties).
    if (
      __DEV__ &&
      enableStrictEffects &&
      (workInProgress.mode & StrictEffectsMode) !== NoMode
    ) {
      workInProgress.flags &= ~(
        MountPassiveDevEffect |
        MountLayoutDevEffect |
        PassiveEffect |
        UpdateEffect
      );
    } else {
      workInProgress.flags &= ~(PassiveEffect | UpdateEffect);
    }
    current.lanes = removeLanes(current.lanes, lanes);
  }
  
  export function resetHooksAfterThrow(): void {
    // We can assume the previous dispatcher is always this one, since we set it
    // at the beginning of the render phase and there's no re-entrancy.
    ReactCurrentDispatcher.current = ContextOnlyDispatcher;
  
    if (didScheduleRenderPhaseUpdate) {
      // There were render phase updates. These are only valid for this render
      // phase, which we are now aborting. Remove the updates from the queues so
      // they do not persist to the next render. Do not remove updates from hooks
      // that weren't processed.
      //
      // Only reset the updates from the queue if it has a clone. If it does
      // not have a clone, that means it wasn't processed, and the updates were
      // scheduled before we entered the render phase.
      let hook: Hook | null = currentlyRenderingFiber.memoizedState;
      while (hook !== null) {
        const queue = hook.queue;
        if (queue !== null) {
          queue.pending = null;
        }
        hook = hook.next;
      }
      didScheduleRenderPhaseUpdate = false;
    }
  
    renderLanes = NoLanes;
    currentlyRenderingFiber = (null: any);
  
    currentHook = null;
    workInProgressHook = null;
  
    if (__DEV__) {
      hookTypesDev = null;
      hookTypesUpdateIndexDev = -1;
  
      currentHookNameInDev = null;
  
      isUpdatingOpaqueValueInRenderPhase = false;
    }
  
    didScheduleRenderPhaseUpdateDuringThisPass = false;
  }
  
  function mountWorkInProgressHook(): Hook {
    // hook数据结构
    const hook: Hook = {
      memoizedState: null, // 存放不同hook的副作用（值）
  
      baseState: null, // 初始state
      baseQueue: null, // 初始队列
      queue: null, // 需要更新的update队列
  
      next: null, // 链表结构指针，用于链接下一个hook
    };
  
    // 如果当前hook为空则重新构建一个hook结构 否则就链接在workInProgressHook链表后面
    if (workInProgressHook === null) {
      // This is the first hook in the list
      // 新建一个hook并挂载在对应fiber节点上
      currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
    } else {
      // Append to the end of the list
      workInProgressHook = workInProgressHook.next = hook;
    }
    return workInProgressHook;
  }
  
  function updateWorkInProgressHook(): Hook {
    // This function is used both for updates and for re-renders triggered by a
    // render phase update. It assumes there is either a current hook we can
    // clone, or a work-in-progress hook from a previous render pass that we can
    // use as a base. When we reach the end of the base list, we must switch to
    // the dispatcher used for mounts.
    let nextCurrentHook: null | Hook;
    if (currentHook === null) {
      const current = currentlyRenderingFiber.alternate;
      if (current !== null) {
        nextCurrentHook = current.memoizedState;
      } else {
        nextCurrentHook = null;
      }
    } else {
      nextCurrentHook = currentHook.next;
    }
  
    let nextWorkInProgressHook: null | Hook;
    if (workInProgressHook === null) {
      nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
    } else {
      nextWorkInProgressHook = workInProgressHook.next;
    }
  
    if (nextWorkInProgressHook !== null) {
      // There's already a work-in-progress. Reuse it.
      workInProgressHook = nextWorkInProgressHook;
      nextWorkInProgressHook = workInProgressHook.next;
  
      currentHook = nextCurrentHook;
    } else {
      // Clone from the current hook.
  
      invariant(
        nextCurrentHook !== null,
        'Rendered more hooks than during the previous render.',
      );
      currentHook = nextCurrentHook;
  
      const newHook: Hook = {
        memoizedState: currentHook.memoizedState,
  
        baseState: currentHook.baseState,
        baseQueue: currentHook.baseQueue,
        queue: currentHook.queue,
  
        next: null,
      };
  
      if (workInProgressHook === null) {
        // This is the first hook in the list.
        currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
      } else {
        // Append to the end of the list.
        workInProgressHook = workInProgressHook.next = newHook;
      }
    }
    return workInProgressHook;
  }
  
  function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
    return {
      lastEffect: null,
    };
  }
  
  function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
    // $FlowFixMe: Flow doesn't like mixed types
    return typeof action === 'function' ? action(state) : action;
  }
  
  function mountReducer<S, I, A>(
    reducer: (S, A) => S,
    initialArg: I,
    init?: I => S,
  ): [S, Dispatch<A>] {
    const hook = mountWorkInProgressHook();
    let initialState;
    if (init !== undefined) {
      initialState = init(initialArg);
    } else {
      initialState = ((initialArg: any): S);
    }
    hook.memoizedState = hook.baseState = initialState;
    const queue = (hook.queue = {
      pending: null,
      interleaved: null,
      lanes: NoLanes,
      dispatch: null,
      lastRenderedReducer: reducer,
      lastRenderedState: (initialState: any),
    });
    const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
      null,
      currentlyRenderingFiber,
      queue,
    ): any));
    return [hook.memoizedState, dispatch];
  }
  
  function updateReducer<S, I, A>(
    reducer: (S, A) => S,
    initialArg: I,
    init?: I => S,
  ): [S, Dispatch<A>] {
    const hook = updateWorkInProgressHook();
    const queue = hook.queue;
    invariant(
      queue !== null,
      'Should have a queue. This is likely a bug in React. Please file an issue.',
    );
  
    queue.lastRenderedReducer = reducer;
  
    const current: Hook = (currentHook: any);
  
    // The last rebase update that is NOT part of the base state.
    let baseQueue = current.baseQueue;
  
    // The last pending update that hasn't been processed yet.
    const pendingQueue = queue.pending;
    if (pendingQueue !== null) {
      // We have new updates that haven't been processed yet.
      // We'll add them to the base queue.
      if (baseQueue !== null) {
        // Merge the pending queue and the base queue.
        const baseFirst = baseQueue.next;
        const pendingFirst = pendingQueue.next;
        baseQueue.next = pendingFirst;
        pendingQueue.next = baseFirst;
      }
      if (__DEV__) {
        if (current.baseQueue !== baseQueue) {
          // Internal invariant that should never happen, but feasibly could in
          // the future if we implement resuming, or some form of that.
          console.error(
            'Internal error: Expected work-in-progress queue to be a clone. ' +
              'This is a bug in React.',
          );
        }
      }
      current.baseQueue = baseQueue = pendingQueue;
      queue.pending = null;
    }
  
    if (baseQueue !== null) {
      // We have a queue to process.
      const first = baseQueue.next;
      let newState = current.baseState;
  
      let newBaseState = null;
      let newBaseQueueFirst = null;
      let newBaseQueueLast = null;
      let update = first;
      do {
        const updateLane = update.lane;
        if (!isSubsetOfLanes(renderLanes, updateLane)) {
          // Priority is insufficient. Skip this update. If this is the first
          // skipped update, the previous update/state is the new base
          // update/state.
          const clone: Update<S, A> = {
            lane: updateLane,
            action: update.action,
            eagerReducer: update.eagerReducer,
            eagerState: update.eagerState,
            next: (null: any),
          };
          if (newBaseQueueLast === null) {
            newBaseQueueFirst = newBaseQueueLast = clone;
            newBaseState = newState;
          } else {
            newBaseQueueLast = newBaseQueueLast.next = clone;
          }
          // Update the remaining priority in the queue.
          // TODO: Don't need to accumulate this. Instead, we can remove
          // renderLanes from the original lanes.
          currentlyRenderingFiber.lanes = mergeLanes(
            currentlyRenderingFiber.lanes,
            updateLane,
          );
          markSkippedUpdateLanes(updateLane);
        } else {
          // This update does have sufficient priority.
  
          if (newBaseQueueLast !== null) {
            const clone: Update<S, A> = {
              // This update is going to be committed so we never want uncommit
              // it. Using NoLane works because 0 is a subset of all bitmasks, so
              // this will never be skipped by the check above.
              lane: NoLane,
              action: update.action,
              eagerReducer: update.eagerReducer,
              eagerState: update.eagerState,
              next: (null: any),
            };
            newBaseQueueLast = newBaseQueueLast.next = clone;
          }
  
          // Process this update.
          if (update.eagerReducer === reducer) {
            // If this update was processed eagerly, and its reducer matches the
            // current reducer, we can use the eagerly computed state.
            newState = ((update.eagerState: any): S);
          } else {
            const action = update.action;
            newState = reducer(newState, action);
          }
        }
        update = update.next;
      } while (update !== null && update !== first);
  
      if (newBaseQueueLast === null) {
        newBaseState = newState;
      } else {
        newBaseQueueLast.next = (newBaseQueueFirst: any);
      }
  
      // Mark that the fiber performed work, but only if the new state is
      // different from the current state.
      if (!is(newState, hook.memoizedState)) {
        markWorkInProgressReceivedUpdate();
      }
  
      hook.memoizedState = newState;
      hook.baseState = newBaseState;
      hook.baseQueue = newBaseQueueLast;
  
      queue.lastRenderedState = newState;
    }
  
    // Interleaved updates are stored on a separate queue. We aren't going to
    // process them during this render, but we do need to track which lanes
    // are remaining.
    const lastInterleaved = queue.interleaved;
    if (lastInterleaved !== null) {
      let interleaved = lastInterleaved;
      do {
        const interleavedLane = interleaved.lane;
        currentlyRenderingFiber.lanes = mergeLanes(
          currentlyRenderingFiber.lanes,
          interleavedLane,
        );
        markSkippedUpdateLanes(interleavedLane);
        interleaved = ((interleaved: any).next: Update<S, A>);
      } while (interleaved !== lastInterleaved);
    } else if (baseQueue === null) {
      // `queue.lanes` is used for entangling transitions. We can set it back to
      // zero once the queue is empty.
      queue.lanes = NoLanes;
    }
  
    const dispatch: Dispatch<A> = (queue.dispatch: any);
    return [hook.memoizedState, dispatch];
  }
  
  function rerenderReducer<S, I, A>(
    reducer: (S, A) => S,
    initialArg: I,
    init?: I => S,
  ): [S, Dispatch<A>] {
    const hook = updateWorkInProgressHook();
    const queue = hook.queue;
    invariant(
      queue !== null,
      'Should have a queue. This is likely a bug in React. Please file an issue.',
    );
  
    queue.lastRenderedReducer = reducer;
  
    // This is a re-render. Apply the new render phase updates to the previous
    // work-in-progress hook.
    const dispatch: Dispatch<A> = (queue.dispatch: any);
    const lastRenderPhaseUpdate = queue.pending;
    let newState = hook.memoizedState;
    if (lastRenderPhaseUpdate !== null) {
      // The queue doesn't persist past this render pass.
      queue.pending = null;
  
      const firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
      let update = firstRenderPhaseUpdate;
      do {
        // Process this render phase update. We don't have to check the
        // priority because it will always be the same as the current
        // render's.
        const action = update.action;
        newState = reducer(newState, action);
        update = update.next;
      } while (update !== firstRenderPhaseUpdate);
  
      // Mark that the fiber performed work, but only if the new state is
      // different from the current state.
      if (!is(newState, hook.memoizedState)) {
        markWorkInProgressReceivedUpdate();
      }
  
      hook.memoizedState = newState;
      // Don't persist the state accumulated from the render phase updates to
      // the base state unless the queue is empty.
      // TODO: Not sure if this is the desired semantics, but it's what we
      // do for gDSFP. I can't remember why.
      if (hook.baseQueue === null) {
        hook.baseState = newState;
      }
  
      queue.lastRenderedState = newState;
    }
    return [newState, dispatch];
  }
  
  type MutableSourceMemoizedState<Source, Snapshot> = {|
    refs: {
      getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
      setSnapshot: Snapshot => void,
    },
    source: MutableSource<any>,
    subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
  |};
  
  function readFromUnsubcribedMutableSource<Source, Snapshot>(
    root: FiberRoot,
    source: MutableSource<Source>,
    getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
  ): Snapshot {
    if (__DEV__) {
      warnAboutMultipleRenderersDEV(source);
    }
  
    const getVersion = source._getVersion;
    const version = getVersion(source._source);
  
    // Is it safe for this component to read from this source during the current render?
    let isSafeToReadFromSource = false;
  
    // Check the version first.
    // If this render has already been started with a specific version,
    // we can use it alone to determine if we can safely read from the source.
    const currentRenderVersion = getWorkInProgressVersion(source);
    if (currentRenderVersion !== null) {
      // It's safe to read if the store hasn't been mutated since the last time
      // we read something.
      isSafeToReadFromSource = currentRenderVersion === version;
    } else {
      // If there's no version, then this is the first time we've read from the
      // source during the current render pass, so we need to do a bit more work.
      // What we need to determine is if there are any hooks that already
      // subscribed to the source, and if so, whether there are any pending
      // mutations that haven't been synchronized yet.
      //
      // If there are no pending mutations, then `root.mutableReadLanes` will be
      // empty, and we know we can safely read.
      //
      // If there *are* pending mutations, we may still be able to safely read
      // if the currently rendering lanes are inclusive of the pending mutation
      // lanes, since that guarantees that the value we're about to read from
      // the source is consistent with the values that we read during the most
      // recent mutation.
      isSafeToReadFromSource = isSubsetOfLanes(
        renderLanes,
        root.mutableReadLanes,
      );
  
      if (isSafeToReadFromSource) {
        // If it's safe to read from this source during the current render,
        // store the version in case other components read from it.
        // A changed version number will let those components know to throw and restart the render.
        setWorkInProgressVersion(source, version);
      }
    }
  
    if (isSafeToReadFromSource) {
      const snapshot = getSnapshot(source._source);
      if (__DEV__) {
        if (typeof snapshot === 'function') {
          console.error(
            'Mutable source should not return a function as the snapshot value. ' +
              'Functions may close over mutable values and cause tearing.',
          );
        }
      }
      return snapshot;
    } else {
      // This handles the special case of a mutable source being shared between renderers.
      // In that case, if the source is mutated between the first and second renderer,
      // The second renderer don't know that it needs to reset the WIP version during unwind,
      // (because the hook only marks sources as dirty if it's written to their WIP version).
      // That would cause this tear check to throw again and eventually be visible to the user.
      // We can avoid this infinite loop by explicitly marking the source as dirty.
      //
      // This can lead to tearing in the first renderer when it resumes,
      // but there's nothing we can do about that (short of throwing here and refusing to continue the render).
      markSourceAsDirty(source);
  
      // Intentioally throw an error to force React to retry synchronously. During
      // the synchronous retry, it will block interleaved mutations, so we should
      // get a consistent read. Therefore, the following error should never be
      // visible to the user.
      //
      // If it were to become visible to the user, it suggests one of two things:
      // a bug in React, or (more likely), a mutation during the render phase that
      // caused the second re-render attempt to be different from the first.
      //
      // We know it's the second case if the logs are currently disabled. So in
      // dev, we can present a more accurate error message.
      if (__DEV__) {
        // eslint-disable-next-line react-internal/no-production-logging
        if (console.log.__reactDisabledLog) {
          // If the logs are disabled, this is the dev-only double render. This is
          // only reachable if there was a mutation during render. Show a helpful
          // error message.
          //
          // Something interesting to note: because we only double render in
          // development, this error will never happen during production. This is
          // actually true of all errors that occur during a double render,
          // because if the first render had thrown, we would have exited the
          // begin phase without double rendering. We should consider suppressing
          // any error from a double render (with a warning) to more closely match
          // the production behavior.
          const componentName = getComponentNameFromFiber(
            currentlyRenderingFiber,
          );
          invariant(
            false,
            'A mutable source was mutated while the %s component was rendering. ' +
              'This is not supported. Move any mutations into event handlers ' +
              'or effects.',
            componentName,
          );
        }
      }
  
      // We expect this error not to be thrown during the synchronous retry,
      // because we blocked interleaved mutations.
      invariant(
        false,
        'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.',
      );
    }
  }
  
  function useMutableSource<Source, Snapshot>(
    hook: Hook,
    source: MutableSource<Source>,
    getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
    subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
  ): Snapshot {
    const root = ((getWorkInProgressRoot(): any): FiberRoot);
    invariant(
      root !== null,
      'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
    );
  
    const getVersion = source._getVersion;
    const version = getVersion(source._source);
  
    const dispatcher = ReactCurrentDispatcher.current;
  
    // eslint-disable-next-line prefer-const
    let [currentSnapshot, setSnapshot] = dispatcher.useState(() =>
      readFromUnsubcribedMutableSource(root, source, getSnapshot),
    );
    let snapshot = currentSnapshot;
  
    // Grab a handle to the state hook as well.
    // We use it to clear the pending update queue if we have a new source.
    const stateHook = ((workInProgressHook: any): Hook);
  
    const memoizedState = ((hook.memoizedState: any): MutableSourceMemoizedState<
      Source,
      Snapshot,
    >);
    const refs = memoizedState.refs;
    const prevGetSnapshot = refs.getSnapshot;
    const prevSource = memoizedState.source;
    const prevSubscribe = memoizedState.subscribe;
  
    const fiber = currentlyRenderingFiber;
  
    hook.memoizedState = ({
      refs,
      source,
      subscribe,
    }: MutableSourceMemoizedState<Source, Snapshot>);
  
    // Sync the values needed by our subscription handler after each commit.
    dispatcher.useEffect(() => {
      refs.getSnapshot = getSnapshot;
  
      // Normally the dispatch function for a state hook never changes,
      // but this hook recreates the queue in certain cases  to avoid updates from stale sources.
      // handleChange() below needs to reference the dispatch function without re-subscribing,
      // so we use a ref to ensure that it always has the latest version.
      refs.setSnapshot = setSnapshot;
  
      // Check for a possible change between when we last rendered now.
      const maybeNewVersion = getVersion(source._source);
      if (!is(version, maybeNewVersion)) {
        const maybeNewSnapshot = getSnapshot(source._source);
        if (__DEV__) {
          if (typeof maybeNewSnapshot === 'function') {
            console.error(
              'Mutable source should not return a function as the snapshot value. ' +
                'Functions may close over mutable values and cause tearing.',
            );
          }
        }
  
        if (!is(snapshot, maybeNewSnapshot)) {
          setSnapshot(maybeNewSnapshot);
  
          const lane = requestUpdateLane(fiber);
          markRootMutableRead(root, lane);
        }
        // If the source mutated between render and now,
        // there may be state updates already scheduled from the old source.
        // Entangle the updates so that they render in the same batch.
        markRootEntangled(root, root.mutableReadLanes);
      }
    }, [getSnapshot, source, subscribe]);
  
    // If we got a new source or subscribe function, re-subscribe in a passive effect.
    dispatcher.useEffect(() => {
      const handleChange = () => {
        const latestGetSnapshot = refs.getSnapshot;
        const latestSetSnapshot = refs.setSnapshot;
  
        try {
          latestSetSnapshot(latestGetSnapshot(source._source));
  
          // Record a pending mutable source update with the same expiration time.
          const lane = requestUpdateLane(fiber);
  
          markRootMutableRead(root, lane);
        } catch (error) {
          // A selector might throw after a source mutation.
          // e.g. it might try to read from a part of the store that no longer exists.
          // In this case we should still schedule an update with React.
          // Worst case the selector will throw again and then an error boundary will handle it.
          latestSetSnapshot(
            (() => {
              throw error;
            }: any),
          );
        }
      };
  
      const unsubscribe = subscribe(source._source, handleChange);
      if (__DEV__) {
        if (typeof unsubscribe !== 'function') {
          console.error(
            'Mutable source subscribe function must return an unsubscribe function.',
          );
        }
      }
  
      return unsubscribe;
    }, [source, subscribe]);
  
    // If any of the inputs to useMutableSource change, reading is potentially unsafe.
    //
    // If either the source or the subscription have changed we can't can't trust the update queue.
    // Maybe the source changed in a way that the old subscription ignored but the new one depends on.
    //
    // If the getSnapshot function changed, we also shouldn't rely on the update queue.
    // It's possible that the underlying source was mutated between the when the last "change" event fired,
    // and when the current render (with the new getSnapshot function) is processed.
    //
    // In both cases, we need to throw away pending updates (since they are no longer relevant)
    // and treat reading from the source as we do in the mount case.
    if (
      !is(prevGetSnapshot, getSnapshot) ||
      !is(prevSource, source) ||
      !is(prevSubscribe, subscribe)
    ) {
      // Create a new queue and setState method,
      // So if there are interleaved updates, they get pushed to the older queue.
      // When this becomes current, the previous queue and dispatch method will be discarded,
      // including any interleaving updates that occur.
      const newQueue = {
        pending: null,
        interleaved: null,
        lanes: NoLanes,
        dispatch: null,
        lastRenderedReducer: basicStateReducer,
        lastRenderedState: snapshot,
      };
      newQueue.dispatch = setSnapshot = (dispatchAction.bind(
        null,
        currentlyRenderingFiber,
        newQueue,
      ): any);
      stateHook.queue = newQueue;
      stateHook.baseQueue = null;
      snapshot = readFromUnsubcribedMutableSource(root, source, getSnapshot);
      stateHook.memoizedState = stateHook.baseState = snapshot;
    }
  
    return snapshot;
  }
  
  function mountMutableSource<Source, Snapshot>(
    source: MutableSource<Source>,
    getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
    subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
  ): Snapshot {
    const hook = mountWorkInProgressHook();
    hook.memoizedState = ({
      refs: {
        getSnapshot,
        setSnapshot: (null: any),
      },
      source,
      subscribe,
    }: MutableSourceMemoizedState<Source, Snapshot>);
    return useMutableSource(hook, source, getSnapshot, subscribe);
  }
  
  function updateMutableSource<Source, Snapshot>(
    source: MutableSource<Source>,
    getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
    subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
  ): Snapshot {
    const hook = updateWorkInProgressHook();
    return useMutableSource(hook, source, getSnapshot, subscribe);
  }
  
  function mountState<S>(
    initialState: (() => S) | S,
  ): [S, Dispatch<BasicStateAction<S>>] {
    // console.log('debug: mountState>>> ', initialState);
    const hook = mountWorkInProgressHook();
    if (typeof initialState === 'function') {
      // $FlowFixMe: Flow doesn't like mixed types
      initialState = initialState();
    }
    hook.memoizedState = hook.baseState = initialState;
    const queue = (hook.queue = {
      pending: null,
      interleaved: null,
      lanes: NoLanes,
      dispatch: null,
      lastRenderedReducer: basicStateReducer,
      lastRenderedState: (initialState: any),
    });
    const dispatch: Dispatch<
      BasicStateAction<S>,
    > = (queue.dispatch = (dispatchAction.bind(
      null,
      currentlyRenderingFiber,
      queue,
    ): any));
    return [hook.memoizedState, dispatch];
  }
  
  function updateState<S>(
    initialState: (() => S) | S,
  ): [S, Dispatch<BasicStateAction<S>>] {
    // console.log('debug: updateState>>> ', initialState);
    return updateReducer(basicStateReducer, (initialState: any));
  }
  
  function rerenderState<S>(
    initialState: (() => S) | S,
  ): [S, Dispatch<BasicStateAction<S>>] {
    return rerenderReducer(basicStateReducer, (initialState: any));
  }
  
  function pushEffect(tag, create, destroy, deps) {
    const effect: Effect = {
      tag,
      create,
      destroy,
      deps,
      // Circular
      next: (null: any),
    };
    let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any);
    if (componentUpdateQueue === null) {
      componentUpdateQueue = createFunctionComponentUpdateQueue();
      currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any);
      componentUpdateQueue.lastEffect = effect.next = effect;
    } else {
      const lastEffect = componentUpdateQueue.lastEffect;
      if (lastEffect === null) {
        componentUpdateQueue.lastEffect = effect.next = effect;
      } else {
        const firstEffect = lastEffect.next;
        lastEffect.next = effect;
        effect.next = firstEffect;
        componentUpdateQueue.lastEffect = effect;
      }
    }
    return effect;
  }
  
  let stackContainsErrorMessage: boolean | null = null;
  
  function getCallerStackFrame(): string {
    const stackFrames = new Error('Error message').stack.split('\n');
  
    // Some browsers (e.g. Chrome) include the error message in the stack
    // but others (e.g. Firefox) do not.
    if (stackContainsErrorMessage === null) {
      stackContainsErrorMessage = stackFrames[0].includes('Error message');
    }
  
    return stackContainsErrorMessage
      ? stackFrames.slice(3, 4).join('\n')
      : stackFrames.slice(2, 3).join('\n');
  }
  
  function mountRef<T>(initialValue: T): {|current: T|} {
    const hook = mountWorkInProgressHook();
    if (enableUseRefAccessWarning) {
      if (__DEV__) {
        // Support lazy initialization pattern shown in docs.
        // We need to store the caller stack frame so that we don't warn on subsequent renders.
        let hasBeenInitialized = initialValue != null;
        let lazyInitGetterStack = null;
        let didCheckForLazyInit = false;
  
        // Only warn once per component+hook.
        let didWarnAboutRead = false;
        let didWarnAboutWrite = false;
  
        let current = initialValue;
        const ref = {
          get current() {
            if (!hasBeenInitialized) {
              didCheckForLazyInit = true;
              lazyInitGetterStack = getCallerStackFrame();
            } else if (currentlyRenderingFiber !== null && !didWarnAboutRead) {
              if (
                lazyInitGetterStack === null ||
                lazyInitGetterStack !== getCallerStackFrame()
              ) {
                didWarnAboutRead = true;
                console.warn(
                  '%s: Unsafe read of a mutable value during render.\n\n' +
                    'Reading from a ref during render is only safe if:\n' +
                    '1. The ref value has not been updated, or\n' +
                    '2. The ref holds a lazily-initialized value that is only set once.\n',
                  getComponentNameFromFiber(currentlyRenderingFiber) || 'Unknown',
                );
              }
            }
            return current;
          },
          set current(value) {
            if (currentlyRenderingFiber !== null && !didWarnAboutWrite) {
              if (
                hasBeenInitialized ||
                (!hasBeenInitialized && !didCheckForLazyInit)
              ) {
                didWarnAboutWrite = true;
                console.warn(
                  '%s: Unsafe write of a mutable value during render.\n\n' +
                    'Writing to a ref during render is only safe if the ref holds ' +
                    'a lazily-initialized value that is only set once.\n',
                  getComponentNameFromFiber(currentlyRenderingFiber) || 'Unknown',
                );
              }
            }
  
            hasBeenInitialized = true;
            current = value;
          },
        };
        Object.seal(ref);
        hook.memoizedState = ref;
        return ref;
      } else {
        const ref = {current: initialValue};
        hook.memoizedState = ref;
        return ref;
      }
    } else {
      const ref = {current: initialValue};
      hook.memoizedState = ref;
      return ref;
    }
  }
  
  function updateRef<T>(initialValue: T): {|current: T|} {
    const hook = updateWorkInProgressHook();
    return hook.memoizedState;
  }
  
  function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
    const hook = mountWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    currentlyRenderingFiber.flags |= fiberFlags;
    hook.memoizedState = pushEffect(
      HookHasEffect | hookFlags,
      create,
      undefined,
      nextDeps,
    );
  }
  
  function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
    const hook = updateWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    let destroy = undefined;
  
    if (currentHook !== null) {
      const prevEffect = currentHook.memoizedState;
      destroy = prevEffect.destroy;
      if (nextDeps !== null) {
        const prevDeps = prevEffect.deps;
        if (areHookInputsEqual(nextDeps, prevDeps)) {
          hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
          return;
        }
      }
    }
  
    currentlyRenderingFiber.flags |= fiberFlags;
  
    hook.memoizedState = pushEffect(
      HookHasEffect | hookFlags,
      create,
      destroy,
      nextDeps,
    );
  }
  
  function mountEffect(
    create: () => (() => void) | void,
    deps: Array<mixed> | void | null,
  ): void {
    console.log('debug: mountEffect>>> ');
    if (__DEV__) {
      // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
      if ('undefined' !== typeof jest) {
        warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber);
      }
    }
    if (
      __DEV__ &&
      enableStrictEffects &&
      (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
    ) {
      return mountEffectImpl(
        MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
        HookPassive,
        create,
        deps,
      );
    } else {
      return mountEffectImpl(
        PassiveEffect | PassiveStaticEffect,
        HookPassive,
        create,
        deps,
      );
    }
  }
  
  function updateEffect(
    create: () => (() => void) | void,
    deps: Array<mixed> | void | null,
  ): void {
    console.log('debug: updateEffect>>> ');
    if (__DEV__) {
      // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
      if ('undefined' !== typeof jest) {
        warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber);
      }
    }
    return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
  }
  
  function mountLayoutEffect(
    create: () => (() => void) | void,
    deps: Array<mixed> | void | null,
  ): void {
    if (
      __DEV__ &&
      enableStrictEffects &&
      (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
    ) {
      return mountEffectImpl(
        MountLayoutDevEffect | UpdateEffect,
        HookLayout,
        create,
        deps,
      );
    } else {
      return mountEffectImpl(UpdateEffect, HookLayout, create, deps);
    }
  }
  
  function updateLayoutEffect(
    create: () => (() => void) | void,
    deps: Array<mixed> | void | null,
  ): void {
    return updateEffectImpl(UpdateEffect, HookLayout, create, deps);
  }
  
  function imperativeHandleEffect<T>(
    create: () => T,
    ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
  ) {
    if (typeof ref === 'function') {
      const refCallback = ref;
      const inst = create();
      refCallback(inst);
      return () => {
        refCallback(null);
      };
    } else if (ref !== null && ref !== undefined) {
      const refObject = ref;
      if (__DEV__) {
        if (!refObject.hasOwnProperty('current')) {
          console.error(
            'Expected useImperativeHandle() first argument to either be a ' +
              'ref callback or React.createRef() object. Instead received: %s.',
            'an object with keys {' + Object.keys(refObject).join(', ') + '}',
          );
        }
      }
      const inst = create();
      refObject.current = inst;
      return () => {
        refObject.current = null;
      };
    }
  }
  
  function mountImperativeHandle<T>(
    ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
    create: () => T,
    deps: Array<mixed> | void | null,
  ): void {
    if (__DEV__) {
      if (typeof create !== 'function') {
        console.error(
          'Expected useImperativeHandle() second argument to be a function ' +
            'that creates a handle. Instead received: %s.',
          create !== null ? typeof create : 'null',
        );
      }
    }
  
    // TODO: If deps are provided, should we skip comparing the ref itself?
    const effectDeps =
      deps !== null && deps !== undefined ? deps.concat([ref]) : null;
  
    if (
      __DEV__ &&
      enableStrictEffects &&
      (currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode
    ) {
      return mountEffectImpl(
        MountLayoutDevEffect | UpdateEffect,
        HookLayout,
        imperativeHandleEffect.bind(null, create, ref),
        effectDeps,
      );
    } else {
      return mountEffectImpl(
        UpdateEffect,
        HookLayout,
        imperativeHandleEffect.bind(null, create, ref),
        effectDeps,
      );
    }
  }
  
  function updateImperativeHandle<T>(
    ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
    create: () => T,
    deps: Array<mixed> | void | null,
  ): void {
    if (__DEV__) {
      if (typeof create !== 'function') {
        console.error(
          'Expected useImperativeHandle() second argument to be a function ' +
            'that creates a handle. Instead received: %s.',
          create !== null ? typeof create : 'null',
        );
      }
    }
  
    // TODO: If deps are provided, should we skip comparing the ref itself?
    const effectDeps =
      deps !== null && deps !== undefined ? deps.concat([ref]) : null;
  
    return updateEffectImpl(
      UpdateEffect,
      HookLayout,
      imperativeHandleEffect.bind(null, create, ref),
      effectDeps,
    );
  }
  
  function mountDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
    // This hook is normally a no-op.
    // The react-debug-hooks package injects its own implementation
    // so that e.g. DevTools can display custom hook values.
  }
  
  const updateDebugValue = mountDebugValue;
  
  function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
    const hook = mountWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    hook.memoizedState = [callback, nextDeps];
    return callback;
  }
  
  function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
    const hook = updateWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    const prevState = hook.memoizedState;
    if (prevState !== null) {
      if (nextDeps !== null) {
        const prevDeps: Array<mixed> | null = prevState[1];
        if (areHookInputsEqual(nextDeps, prevDeps)) {
          return prevState[0];
        }
      }
    }
    hook.memoizedState = [callback, nextDeps];
    return callback;
  }
  
  function mountMemo<T>(
    nextCreate: () => T,
    deps: Array<mixed> | void | null,
  ): T {
    const hook = mountWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    const nextValue = nextCreate();
    hook.memoizedState = [nextValue, nextDeps];
    return nextValue;
  }
  
  function updateMemo<T>(
    nextCreate: () => T,
    deps: Array<mixed> | void | null,
  ): T {
    const hook = updateWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    const prevState = hook.memoizedState;
    if (prevState !== null) {
      // Assume these are defined. If they're not, areHookInputsEqual will warn.
      if (nextDeps !== null) {
        const prevDeps: Array<mixed> | null = prevState[1];
        if (areHookInputsEqual(nextDeps, prevDeps)) {
          return prevState[0];
        }
      }
    }
    const nextValue = nextCreate();
    hook.memoizedState = [nextValue, nextDeps];
    return nextValue;
  }
  
  function mountDeferredValue<T>(value: T): T {
    const [prevValue, setValue] = mountState(value);
    mountEffect(() => {
      const prevTransition = ReactCurrentBatchConfig.transition;
      ReactCurrentBatchConfig.transition = 1;
      try {
        setValue(value);
      } finally {
        ReactCurrentBatchConfig.transition = prevTransition;
      }
    }, [value]);
    return prevValue;
  }
  
  function updateDeferredValue<T>(value: T): T {
    const [prevValue, setValue] = updateState(value);
    updateEffect(() => {
      const prevTransition = ReactCurrentBatchConfig.transition;
      ReactCurrentBatchConfig.transition = 1;
      try {
        setValue(value);
      } finally {
        ReactCurrentBatchConfig.transition = prevTransition;
      }
    }, [value]);
    return prevValue;
  }
  
  function rerenderDeferredValue<T>(value: T): T {
    const [prevValue, setValue] = rerenderState(value);
    updateEffect(() => {
      const prevTransition = ReactCurrentBatchConfig.transition;
      ReactCurrentBatchConfig.transition = 1;
      try {
        setValue(value);
      } finally {
        ReactCurrentBatchConfig.transition = prevTransition;
      }
    }, [value]);
    return prevValue;
  }
  
  function startTransition(setPending, callback) {
    const previousLanePriority = getCurrentUpdateLanePriority();
    setCurrentUpdateLanePriority(
      higherLanePriority(previousLanePriority, InputContinuousLanePriority),
    );
  
    setPending(true);
  
    // TODO: Can remove this. Was only necessary because we used to give
    // different behavior to transitions without a config object. Now they are
    // all treated the same.
    setCurrentUpdateLanePriority(DefaultLanePriority);
  
    const prevTransition = ReactCurrentBatchConfig.transition;
    ReactCurrentBatchConfig.transition = 1;
    try {
      setPending(false);
      callback();
    } finally {
      setCurrentUpdateLanePriority(previousLanePriority);
      ReactCurrentBatchConfig.transition = prevTransition;
    }
  }
  
  function mountTransition(): [(() => void) => void, boolean] {
    const [isPending, setPending] = mountState(false);
    // The `start` method never changes.
    const start = startTransition.bind(null, setPending);
    const hook = mountWorkInProgressHook();
    hook.memoizedState = start;
    return [start, isPending];
  }
  
  function updateTransition(): [(() => void) => void, boolean] {
    const [isPending] = updateState(false);
    const hook = updateWorkInProgressHook();
    const start = hook.memoizedState;
    return [start, isPending];
  }
  
  function rerenderTransition(): [(() => void) => void, boolean] {
    const [isPending] = rerenderState(false);
    const hook = updateWorkInProgressHook();
    const start = hook.memoizedState;
    return [start, isPending];
  }
  
  let isUpdatingOpaqueValueInRenderPhase = false;
  export function getIsUpdatingOpaqueValueInRenderPhaseInDEV(): boolean | void {
    if (__DEV__) {
      return isUpdatingOpaqueValueInRenderPhase;
    }
  }
  
  function warnOnOpaqueIdentifierAccessInDEV(fiber) {
    if (__DEV__) {
      // TODO: Should warn in effects and callbacks, too
      const name = getComponentNameFromFiber(fiber) || 'Unknown';
      if (getIsRendering() && !didWarnAboutUseOpaqueIdentifier[name]) {
        console.error(
          'The object passed back from useOpaqueIdentifier is meant to be ' +
            'passed through to attributes only. Do not read the ' +
            'value directly.',
        );
        didWarnAboutUseOpaqueIdentifier[name] = true;
      }
    }
  }
  
  function mountOpaqueIdentifier(): OpaqueIDType | void {
    const makeId = __DEV__
      ? makeClientIdInDEV.bind(
          null,
          warnOnOpaqueIdentifierAccessInDEV.bind(null, currentlyRenderingFiber),
        )
      : makeClientId;
  
    if (getIsHydrating()) {
      let didUpgrade = false;
      const fiber = currentlyRenderingFiber;
      const readValue = () => {
        if (!didUpgrade) {
          // Only upgrade once. This works even inside the render phase because
          // the update is added to a shared queue, which outlasts the
          // in-progress render.
          didUpgrade = true;
          if (__DEV__) {
            isUpdatingOpaqueValueInRenderPhase = true;
            setId(makeId());
            isUpdatingOpaqueValueInRenderPhase = false;
            warnOnOpaqueIdentifierAccessInDEV(fiber);
          } else {
            setId(makeId());
          }
        }
        invariant(
          false,
          'The object passed back from useOpaqueIdentifier is meant to be ' +
            'passed through to attributes only. Do not read the value directly.',
        );
      };
      const id = makeOpaqueHydratingObject(readValue);
  
      const setId = mountState(id)[1];
  
      if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) {
        if (
          __DEV__ &&
          enableStrictEffects &&
          (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode
        ) {
          currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect;
        } else {
          currentlyRenderingFiber.flags |= PassiveEffect;
        }
        pushEffect(
          HookHasEffect | HookPassive,
          () => {
            setId(makeId());
          },
          undefined,
          null,
        );
      }
      return id;
    } else {
      const id = makeId();
      mountState(id);
      return id;
    }
  }
  
  function updateOpaqueIdentifier(): OpaqueIDType | void {
    const id = updateState(undefined)[0];
    return id;
  }
  
  function rerenderOpaqueIdentifier(): OpaqueIDType | void {
    const id = rerenderState(undefined)[0];
    return id;
  }
  
  function mountRefresh() {
    const hook = mountWorkInProgressHook();
    const refresh = (hook.memoizedState = refreshCache.bind(
      null,
      currentlyRenderingFiber,
    ));
    return refresh;
  }
  
  function updateRefresh() {
    const hook = updateWorkInProgressHook();
    return hook.memoizedState;
  }
  
  function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T) {
    // TODO: Does Cache work in legacy mode? Should decide and write a test.
    // TODO: Consider warning if the refresh is at discrete priority, or if we
    // otherwise suspect that it wasn't batched properly.
    let provider = fiber.return;
    while (provider !== null) {
      switch (provider.tag) {
        case CacheComponent:
        case HostRoot: {
          const lane = requestUpdateLane(provider);
          const eventTime = requestEventTime();
          const root = scheduleUpdateOnFiber(provider, lane, eventTime);
          if (root !== null) {
            entangleTransitions(root, provider, lane);
          }
  
          const seededCache = new Map();
          if (seedKey !== null && seedKey !== undefined && root !== null) {
            // Seed the cache with the value passed by the caller. This could be
            // from a server mutation, or it could be a streaming response.
            seededCache.set(seedKey, seedValue);
          }
  
          // Schedule an update on the cache boundary to trigger a refresh.
          const refreshUpdate = createUpdate(eventTime, lane);
          const payload = {
            cache: seededCache,
          };
          refreshUpdate.payload = payload;
          enqueueUpdate(provider, refreshUpdate, lane);
          return;
        }
      }
      provider = provider.return;
    }
    // TODO: Warn if unmounted?
  }
  
  function dispatchAction<S, A>(
    fiber: Fiber,
    queue: UpdateQueue<S, A>,
    action: A,
  ) {
    // console.log('debug: dispatchAction>>> ', action);
    if (__DEV__) {
      if (typeof arguments[3] === 'function') {
        console.error(
          "State updates from the useState() and useReducer() Hooks don't support the " +
            'second callback argument. To execute a side effect after ' +
            'rendering, declare it in the component body with useEffect().',
        );
      }
    }
  
    const eventTime = requestEventTime();
    const lane = requestUpdateLane(fiber);
  
    const update: Update<S, A> = {
      lane,
      action,
      eagerReducer: null,
      eagerState: null,
      next: (null: any),
    };
  
    const alternate = fiber.alternate;
    if (
      fiber === currentlyRenderingFiber ||
      (alternate !== null && alternate === currentlyRenderingFiber)
    ) {
      // This is a render phase update. Stash it in a lazily-created map of
      // queue -> linked list of updates. After this render pass, we'll restart
      // and apply the stashed updates on top of the work-in-progress hook.
      didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
      const pending = queue.pending;
      if (pending === null) {
        // This is the first update. Create a circular list.
        update.next = update;
      } else {
        update.next = pending.next;
        pending.next = update;
      }
      queue.pending = update;
    } else {
      if (isInterleavedUpdate(fiber, lane)) {
        const interleaved = queue.interleaved;
        if (interleaved === null) {
          // This is the first update. Create a circular list.
          update.next = update;
          // At the end of the current render, this queue's interleaved updates will
          // be transfered to the pending queue.
          pushInterleavedQueue(queue);
        } else {
          update.next = interleaved.next;
          interleaved.next = update;
        }
        queue.interleaved = update;
      } else {
        const pending = queue.pending;
        if (pending === null) {
          // This is the first update. Create a circular list.
          update.next = update;
        } else {
          update.next = pending.next;
          pending.next = update;
        }
        queue.pending = update;
      }
  
      if (
        fiber.lanes === NoLanes &&
        (alternate === null || alternate.lanes === NoLanes)
      ) {
        // The queue is currently empty, which means we can eagerly compute the
        // next state before entering the render phase. If the new state is the
        // same as the current state, we may be able to bail out entirely.
        const lastRenderedReducer = queue.lastRenderedReducer;
        if (lastRenderedReducer !== null) {
          let prevDispatcher;
          if (__DEV__) {
            prevDispatcher = ReactCurrentDispatcher.current;
            ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
          }
          try {
            const currentState: S = (queue.lastRenderedState: any);
            const eagerState = lastRenderedReducer(currentState, action);
            // Stash the eagerly computed state, and the reducer used to compute
            // it, on the update object. If the reducer hasn't changed by the
            // time we enter the render phase, then the eager state can be used
            // without calling the reducer again.
            update.eagerReducer = lastRenderedReducer;
            update.eagerState = eagerState;
            if (is(eagerState, currentState)) {
              // Fast path. We can bail out without scheduling React to re-render.
              // It's still possible that we'll need to rebase this update later,
              // if the component re-renders for a different reason and by that
              // time the reducer has changed.
              return;
            }
          } catch (error) {
            // Suppress the error. It will throw again in the render phase.
          } finally {
            if (__DEV__) {
              ReactCurrentDispatcher.current = prevDispatcher;
            }
          }
        }
      }
      if (__DEV__) {
        // $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
        if ('undefined' !== typeof jest) {
          warnIfNotScopedWithMatchingAct(fiber);
          warnIfNotCurrentlyActingUpdatesInDev(fiber);
        }
      }
      const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
  
      if (isTransitionLane(lane) && root !== null) {
        let queueLanes = queue.lanes;
  
        // If any entangled lanes are no longer pending on the root, then they
        // must have finished. We can remove them from the shared queue, which
        // represents a superset of the actually pending lanes. In some cases we
        // may entangle more than we need to, but that's OK. In fact it's worse if
        // we *don't* entangle when we should.
        queueLanes = intersectLanes(queueLanes, root.pendingLanes);
  
        // Entangle the new transition lane with the other transition lanes.
        const newQueueLanes = mergeLanes(queueLanes, lane);
        queue.lanes = newQueueLanes;
        // Even if queue.lanes already include lane, we don't know for certain if
        // the lane finished since the last time we entangled it. So we need to
        // entangle it again, just to be sure.
        markRootEntangled(root, newQueueLanes);
      }
    }
  
    if (__DEV__) {
      if (enableDebugTracing) {
        if (fiber.mode & DebugTracingMode) {
          const name = getComponentNameFromFiber(fiber) || 'Unknown';
          logStateUpdateScheduled(name, lane, action);
        }
      }
    }
  
    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  }
  
  function getCacheForType<T>(resourceType: () => T): T {
    if (!enableCache) {
      invariant(false, 'Not implemented.');
    }
    const cache: Cache = readContext(CacheContext);
    let cacheForType: T | void = (cache.get(resourceType): any);
    if (cacheForType === undefined) {
      cacheForType = resourceType();
      cache.set(resourceType, cacheForType);
    }
    return cacheForType;
  }
  
  export const ContextOnlyDispatcher: Dispatcher = {
    readContext,
  
    useCallback: throwInvalidHookError,
    useContext: throwInvalidHookError,
    useEffect: throwInvalidHookError,
    useImperativeHandle: throwInvalidHookError,
    useLayoutEffect: throwInvalidHookError,
    useMemo: throwInvalidHookError,
    useReducer: throwInvalidHookError,
    useRef: throwInvalidHookError,
    useState: throwInvalidHookError,
    useDebugValue: throwInvalidHookError,
    useDeferredValue: throwInvalidHookError,
    useTransition: throwInvalidHookError,
    useMutableSource: throwInvalidHookError,
    useOpaqueIdentifier: throwInvalidHookError,
  
    unstable_isNewReconciler: enableNewReconciler,
  };
  if (enableCache) {
    (ContextOnlyDispatcher: Dispatcher).getCacheForType = getCacheForType;
    (ContextOnlyDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError;
  }
  
  const HooksDispatcherOnMount: Dispatcher = {
    readContext,
  
    useCallback: mountCallback,
    useContext: readContext,
    useEffect: mountEffect,
    useImperativeHandle: mountImperativeHandle,
    useLayoutEffect: mountLayoutEffect,
    useMemo: mountMemo,
    useReducer: mountReducer,
    useRef: mountRef,
    useState: mountState,
    useDebugValue: mountDebugValue,
    useDeferredValue: mountDeferredValue,
    useTransition: mountTransition,
    useMutableSource: mountMutableSource,
    useOpaqueIdentifier: mountOpaqueIdentifier,
  
    unstable_isNewReconciler: enableNewReconciler,
  };
  if (enableCache) {
    (HooksDispatcherOnMount: Dispatcher).getCacheForType = getCacheForType;
    (HooksDispatcherOnMount: Dispatcher).useCacheRefresh = mountRefresh;
  }
  
  const HooksDispatcherOnUpdate: Dispatcher = {
    readContext,
  
    useCallback: updateCallback,
    useContext: readContext,
    useEffect: updateEffect,
    useImperativeHandle: updateImperativeHandle,
    useLayoutEffect: updateLayoutEffect,
    useMemo: updateMemo,
    useReducer: updateReducer,
    useRef: updateRef,
    useState: updateState,
    useDebugValue: updateDebugValue,
    useDeferredValue: updateDeferredValue,
    useTransition: updateTransition,
    useMutableSource: updateMutableSource,
    useOpaqueIdentifier: updateOpaqueIdentifier,
  
    unstable_isNewReconciler: enableNewReconciler,
  };
  if (enableCache) {
    (HooksDispatcherOnUpdate: Dispatcher).getCacheForType = getCacheForType;
    (HooksDispatcherOnUpdate: Dispatcher).useCacheRefresh = updateRefresh;
  }
  
  const HooksDispatcherOnRerender: Dispatcher = {
    readContext,
  
    useCallback: updateCallback,
    useContext: readContext,
    useEffect: updateEffect,
    useImperativeHandle: updateImperativeHandle,
    useLayoutEffect: updateLayoutEffect,
    useMemo: updateMemo,
    useReducer: rerenderReducer,
    useRef: updateRef,
    useState: rerenderState,
    useDebugValue: updateDebugValue,
    useDeferredValue: rerenderDeferredValue,
    useTransition: rerenderTransition,
    useMutableSource: updateMutableSource,
    useOpaqueIdentifier: rerenderOpaqueIdentifier,
  
    unstable_isNewReconciler: enableNewReconciler,
  };
  if (enableCache) {
    (HooksDispatcherOnRerender: Dispatcher).getCacheForType = getCacheForType;
    (HooksDispatcherOnRerender: Dispatcher).useCacheRefresh = updateRefresh;
  }
  
  let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
  let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null;
  let HooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
  let HooksDispatcherOnRerenderInDEV: Dispatcher | null = null;
  let InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher | null = null;
  let InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
  let InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher | null = null;
  
  if (__DEV__) {
    const warnInvalidContextAccess = () => {
      console.error(
        'Context can only be read while React is rendering. ' +
          'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
          'In function components, you can read it directly in the function body, but not ' +
          'inside Hooks like useReducer() or useMemo().',
      );
    };
  
    const warnInvalidHookAccess = () => {
      console.error(
        'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' +
          'You can only call Hooks at the top level of your React function. ' +
          'For more information, see ' +
          'https://reactjs.org/link/rules-of-hooks',
      );
    };
  
    HooksDispatcherOnMountInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        return readContext(context, observedBits);
      },
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        mountHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        return mountLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        mountHookTypesDev();
        checkDepsAreArrayDev(deps);
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        mountHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        mountHookTypesDev();
        return mountRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        mountHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        mountHookTypesDev();
        return mountDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        mountHookTypesDev();
        return mountDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        mountHookTypesDev();
        return mountTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        mountHookTypesDev();
        return mountMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        mountHookTypesDev();
        return mountOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (HooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType;
      (HooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        mountHookTypesDev();
        return mountRefresh();
      };
    }
  
    HooksDispatcherOnMountWithHookTypesInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        return readContext(context, observedBits);
      },
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        updateHookTypesDev();
        return mountCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        updateHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        updateHookTypesDev();
        return mountEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        updateHookTypesDev();
        return mountImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        updateHookTypesDev();
        return mountLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        updateHookTypesDev();
        return mountRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        updateHookTypesDev();
        return mountDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        updateHookTypesDev();
        return mountDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        updateHookTypesDev();
        return mountTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        updateHookTypesDev();
        return mountMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        updateHookTypesDev();
        return mountOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).getCacheForType = getCacheForType;
      (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return mountRefresh();
      };
    }
  
    HooksDispatcherOnUpdateInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        return readContext(context, observedBits);
      },
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        updateHookTypesDev();
        return updateCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        updateHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        updateHookTypesDev();
        return updateEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        updateHookTypesDev();
        return updateImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        updateHookTypesDev();
        return updateLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        updateHookTypesDev();
        return updateRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        updateHookTypesDev();
        return updateDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        updateHookTypesDev();
        return updateDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        updateHookTypesDev();
        return updateTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        updateHookTypesDev();
        return updateMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        updateHookTypesDev();
        return updateOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (HooksDispatcherOnUpdateInDEV: Dispatcher).getCacheForType = getCacheForType;
      (HooksDispatcherOnUpdateInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return updateRefresh();
      };
    }
  
    HooksDispatcherOnRerenderInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        return readContext(context, observedBits);
      },
  
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        updateHookTypesDev();
        return updateCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        updateHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        updateHookTypesDev();
        return updateEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        updateHookTypesDev();
        return updateImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        updateHookTypesDev();
        return updateLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
        try {
          return updateMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
        try {
          return rerenderReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        updateHookTypesDev();
        return updateRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
        try {
          return rerenderState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        updateHookTypesDev();
        return updateDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        updateHookTypesDev();
        return rerenderDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        updateHookTypesDev();
        return rerenderTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        updateHookTypesDev();
        return updateMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        updateHookTypesDev();
        return rerenderOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (HooksDispatcherOnRerenderInDEV: Dispatcher).getCacheForType = getCacheForType;
      (HooksDispatcherOnRerenderInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return updateRefresh();
      };
    }
  
    InvalidNestedHooksDispatcherOnMountInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        warnInvalidContextAccess();
        return readContext(context, observedBits);
      },
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        warnInvalidHookAccess();
        mountHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        warnInvalidHookAccess();
        mountHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        warnInvalidHookAccess();
        mountHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
        try {
          return mountState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        warnInvalidHookAccess();
        mountHookTypesDev();
        return mountOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).getCacheForType = getCacheForType;
      (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return mountRefresh();
      };
    }
  
    InvalidNestedHooksDispatcherOnUpdateInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        warnInvalidContextAccess();
        return readContext(context, observedBits);
      },
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).getCacheForType = getCacheForType;
      (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return updateRefresh();
      };
    }
  
    InvalidNestedHooksDispatcherOnRerenderInDEV = {
      readContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        warnInvalidContextAccess();
        return readContext(context, observedBits);
      },
  
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useCallback';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateCallback(callback, deps);
      },
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T {
        currentHookNameInDev = 'useContext';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return readContext(context, observedBits);
      },
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useEffect';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateEffect(create, deps);
      },
      useImperativeHandle<T>(
        ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
        create: () => T,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useImperativeHandle';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateImperativeHandle(ref, create, deps);
      },
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void {
        currentHookNameInDev = 'useLayoutEffect';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateLayoutEffect(create, deps);
      },
      useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
        currentHookNameInDev = 'useMemo';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return updateMemo(create, deps);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: I => S,
      ): [S, Dispatch<A>] {
        currentHookNameInDev = 'useReducer';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return rerenderReducer(reducer, initialArg, init);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useRef<T>(initialValue: T): {|current: T|} {
        currentHookNameInDev = 'useRef';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateRef(initialValue);
      },
      useState<S>(
        initialState: (() => S) | S,
      ): [S, Dispatch<BasicStateAction<S>>] {
        currentHookNameInDev = 'useState';
        warnInvalidHookAccess();
        updateHookTypesDev();
        const prevDispatcher = ReactCurrentDispatcher.current;
        ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
        try {
          return rerenderState(initialState);
        } finally {
          ReactCurrentDispatcher.current = prevDispatcher;
        }
      },
      useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
        currentHookNameInDev = 'useDebugValue';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateDebugValue(value, formatterFn);
      },
      useDeferredValue<T>(value: T): T {
        currentHookNameInDev = 'useDeferredValue';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return rerenderDeferredValue(value);
      },
      useTransition(): [(() => void) => void, boolean] {
        currentHookNameInDev = 'useTransition';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return rerenderTransition();
      },
      useMutableSource<Source, Snapshot>(
        source: MutableSource<Source>,
        getSnapshot: MutableSourceGetSnapshotFn<Source, Snapshot>,
        subscribe: MutableSourceSubscribeFn<Source, Snapshot>,
      ): Snapshot {
        currentHookNameInDev = 'useMutableSource';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return updateMutableSource(source, getSnapshot, subscribe);
      },
      useOpaqueIdentifier(): OpaqueIDType | void {
        currentHookNameInDev = 'useOpaqueIdentifier';
        warnInvalidHookAccess();
        updateHookTypesDev();
        return rerenderOpaqueIdentifier();
      },
  
      unstable_isNewReconciler: enableNewReconciler,
    };
    if (enableCache) {
      (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).getCacheForType = getCacheForType;
      (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useCacheRefresh = function useCacheRefresh() {
        currentHookNameInDev = 'useCacheRefresh';
        updateHookTypesDev();
        return updateRefresh();
      };
    }
  }
  