• react的useState源码分析


    React暴露出来的部分Hooks

    //packages/react/src/React.js
    export {
       
      ...
      useCallback,
      useContext,
      useEffect,
      useLayoutEffect,
      useMemo,
      useReducer,
      useRef,
      useState,
      ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    功能描述

    • useStateuseReducer: 状态值相关
    • useEffectuseLayoutEffect: 生命周期相关
    • useContext: 状态共享相关
    • useCallbackuseMemo: 性能优化相关
    • useRef: 属性相关

    源码

    export function useContext<T>(
      Context: ReactContext<T>,
      unstable_observedBits: number | boolean | void,
    ): T {
       
      const dispatcher = resolveDispatcher();
      ...
      return dispatcher.useContext(Context, unstable_observedBits);
    }
    
    export function useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useState(initialState);
    }
    
    export function useReducer<S, I, A>(
      reducer: (S, A) => S,
      initialArg: I,
      init?: I => S,
    ): [S, Dispatch<A>] {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useReducer(reducer, initialArg, init);
    }
    
    export function useRef<T>(initialValue: T): {
       |current: T|} {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useRef(initialValue);
    }
    
    export function useEffect(
      create: () => (() => void) | void,  deps: Array<mixed> | void | null,
    ): void {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useEffect(create, deps);
    }
    
    export function useLayoutEffect(
      create: () => (() => void) | void,  deps: Array<mixed> | void | null,
    ): void {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useLayoutEffect(create, deps);
    }
    
    export function useCallback<T>(
      callback: T,
      deps: Array<mixed> | void | null,
    ): T {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useCallback(callback, deps);
    }
    
    export function useMemo<T>(
      create: () => T,
      deps: Array<mixed> | void | null,
    ): T {
       
      const dispatcher = resolveDispatcher();
      return dispatcher.useMemo(create, deps);
    }
    
    
    // resolveDispatcher
    function resolveDispatcher() {
       
      const dispatcher = ReactCurrentDispatcher.current;
      invariant(
       ...
      );
      return dispatcher;
    }
    
    // ReactCurrentDispatcher
    const ReactCurrentDispatcher = {
       
      /**   * @internal
       * @type {ReactComponent}   */
      current: (null: null | Dispatcher),
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    相关参考视频讲解:进入学习

    其实hooks的定义都来自dispatcher,那我们根据Dispatcher依次去看看他们的实际实现。

    Dispatcher

    export type Dispatcher = {
       |
      ...
      useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>],
      useReducer<S, I, A>(
        reducer: (S, A) => S,
        initialArg: I,
        init?: (I) => S,
      ): [S, Dispatch<A>],
      useContext<T>(
        context: ReactContext<T>,
        observedBits: void | number | boolean,
      ): T,
      useRef<T>(initialValue: T): {
       |current: T|},
      useEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void,
      useLayoutEffect(
        create: () => (() => void) | void,
        deps: Array<mixed> | void | null,
      ): void,
      useCallback<T>(callback: T, deps: Array<mixed> | void | null): T,
      useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T,
      ...
    |};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    很明显这就是各种hooks的定义,但是总归都要是参加到执行的的流程里面去的,函数组件也属于ReactComponent的一种,他也有mountupdate阶段。

    函数组件Mount阶段

    我们在前面提到执行beginWork函数中,我们发现会有tagFunctionComponent的选项,他会调用updateFunctionComponent进而调用renderWithHooks进行更新。

    // packages/react-reconciler/src/ReactFiberBeginWork.old.js
    function beginWork(
      current: Fiber | null,  workInProgress: Fiber,  renderLanes: Lanes,
    ): Fiber | null {
       
      const updateLanes = workInProgress.lanes;
      switch (workInProgress.tag) {
       
        case FunctionComponent: {
       
          const Component = workInProgress.type; // 组件类型
          const unresolvedProps = workInProgress.pendingProps; // 未处理的props
          ...
          return updateFunctionComponent(
            current,
            workInProgress,
            Component,
            resolvedProps,
            renderLanes,
          );
        }
        case ClassComponent: {
       
          const Component = workInProgress.type;
          const unresolvedProps = workInProgress.pendingProps;
          const resolvedProps =
            workInProgress.elementType === Component
              ? unresolvedProps
              : resolveDefaultProps(Component, unresolvedProps);
              // 渲染classComponent
          return updateClassComponent(
            current,
            workInProgress,
            Component,
            resolvedProps,
            renderLanes,
          );
        }
        case HostRoot:
          ...
        case HostComponent:
          ...
        case HostText:
          ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    renderWithHooks

    export function renderWithHooks<Props, SecondArg>(
      current: Fiber | null,
      workInProgress: Fiber,
      Component: (p: Props, arg: SecondArg) => any,
      props: Props,
      secondArg: SecondArg,
      nextRenderLanes: Lanes,
    ): any {
       
      // 下一个渲染任务的优先级
      renderLanes = nextRenderLanes;
      // fiber树
      currentlyRenderingFiber = workInProgress;
    
      ...
      // 重置memoizedState、updateQueue、lanes
      workInProgress.memoizedState = null;
      workInProgress.updateQueue = null;
      workInProgress.lanes = NoLanes;
      ...
    
      if (__DEV__) {
       
        ...
      } else {
       
        // 判断mount update阶段
        ReactCurrentDispatcher.current =
          current === null || current.memoizedState === null
            ? HooksDispatcherOnMount
            : HooksDispatcherOnUpdate;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 如果是mount阶段,则执行HooksDispatcherOnMount,之后再执行reconcileChildren
    • 如果是update阶段,则执行HooksDispatcherOnUpdate,之后再执行reconcileChildren

    HooksDispatcherOnMount

    const HooksDispatcherOnMount: Dispatcher = {
       
      useCallback: mountCallback,
      useContext: readContext,
      useEffect: mountEffect,
      useLayoutEffect: mountLayoutEffect,
      useMemo: mountMemo,
      useReducer: mountReducer,
      useRef: mountRef,
      useState: mountState,
      ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    mount的情况下,每个hook是都有自己对应的mountXxxuseState的对应的就是mountState,不过在讲mountState之前我们要去看一个东西 – type hook,因为实际的开发当中不可能只用一个hook,多个hook他就会形成链表,保存在fiber的memoizedState上面。

    type Hook

    export type Hook = {
       |
      memoizedState: any, // 单独的hook唯一值
      baseState: any, // 初始state
      baseQueue: Update<any, any> | null, // 初始更新队列
      queue: UpdateQueue<any, any> | null, // 更新队列
      next: Hook | null, // hooks链表下一个指针
    |};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    那么我们去看看他是如何关联起来的:const hook = mountWorkInProgressHook()

    function mountWorkInProgressHook(): Hook {
       
      const hook: Hook = {
       
        memoizedState: null,
        baseState: null,
        baseQueue: null,
        queue: null,
        next: null,
      };
    
      if (workInProgressHook === null) {
       
        // This is the first hook in the list
        // 第一个hook
        currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
      } else {
       
        // Append to the end of the list
        // 往后面添加,形成链表
        workInProgressHook = workInProgressHook.next = hook;
      }
      return workInProgressHook;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    mountState

    function mountState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
       
    
      // 创建并关联hook链表  
      const hook = mountWorkInProgressHook();
      // 如果是函数入参,则是把函数的执行结果当做入参
      if (typeof initialState === 'function') {
       
        // $FlowFixMe: Flow doesn't like mixed types
        initialState = initialState();
      }
      // 把hook的memoizedState变为初始值
      hook.memoizedState = hook.baseState = initialState;
    
      // 更新队列
      const queue = (hook.queue = {
       
        pending: null,
        dispatch: null,
        lastRenderedReducer: basicStateReducer,
        lastRenderedState: (initialState: any),
      });
      // 回调
      const dispatch: Dispatch<
        BasicStateAction<S>,
      > = (queue.dispatch = (dispatchAction.bind(
        null,
        currentlyRenderingFiber,
        queue,
      ): any));
      // 返回 memoizedState, dispatch
      return [hook.memoizedState, dispatch];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    根据mountState我们就知道在写useState的时候,解构的语法为什么会解构出想要的东西了。

    const [mutation, setMutation] = useState(0) // 纯数字作为初始值

    const [mutation, setMutation] = useState(()=>handle(1)) // 函数作为初始值,函数的返回值作为初始值

    这里我们遗留了一个问题

    • 我们知道第二个参数dispatch执行的时候会触发渲染更新,以及二次更新,那么他是怎么实现的呢?

    针对于上述问题,我们必须去看一下dispatch到底干了什么事情。

    const dispatch: Dispatch<
        BasicStateAction<S>,
      > = (queue.dispatch = (dispatchAction.bind(
        null,
        currentlyRenderingFiber,
        queue,
      ): any));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    dispatchAction

    function dispatchAction<S, A>(
      fiber: Fiber,
      queue: UpdateQueue<S, A>,
      action: A,
    ) {
       
      if (__DEV__) {
       
        ...
      }
      // 获取触发更新的时间
      const eventTime = requestEventTime();
      // 获取更新优先级
      const lane = requestUpdateLane(fiber);
    
      // 创建更新
      const update: Update<S, A> = {
       
        lane,
        action,
        eagerReducer: null,
        eagerState: null,
        next: (null: any),
      };
    
      // 维护链表,在update阶段,如果有更新任务则一直延续
      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;
    
      const alternate 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    【开发工具套件与Web图表工具】上海道宁为您带来Visual Paradigm工具软件,推动IT项目的开发与成功
    JAVA设计模式 —— 软件设计六大原则
    网络安全(黑客)-高效自学
    LeetCode·每日一题·654.最大二叉树·递归·单调栈
    TouchGFX之Mixins
    手把手教你Magisk安装
    计算机毕设(附源码)JAVA-SSM基于的学生事务管理系统
    牛客网刷题-(2)
    ROS-Noetic安装与环境搭建-Ubuntu20
    java毕业设计点播影院运营系统(附源码、数据库)
  • 原文地址:https://blog.csdn.net/weixin_59558923/article/details/127781759