• 浅析React Hook之useReducer


    浅析React Hook之useReducer

    提示:本文主要讲解useReducer



    认识Fiber

    Fiber是什么?

    Fiber是一种数据结构,用于描述虚拟DOM。
    react目前的做法是使用链表。每个virtualDOM节点内部表示为一个Fiber

    Fiber树

    顾名思义,将Fiber连接起来就是Fiber树
    在这里插入图片描述

    Fiber是一个执行单单元

    Fiber是一个执行单元,每次执行完一个执行单元, React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去

    认识循环链表

    循环链表是另一种形式的链式存储结构

    它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环
    在这里插入图片描述

    思考:这种结构该如何实现???

    代码如下(示例):

    function enqueueUpdate(queue, action){
    	const update = {action, next:null}
    	//TODO ?
    }
    
    • 1
    • 2
    • 3
    • 4

    认识Hook

    什么是hook?

    Hook是React 16.8的新增特性。它可以让你在不便携class的情况下使用state以及其他的React特性。

    为什么会引入hook

    官方给出的动机是解决长时间使用和维护react过程中经常遇到的问题。
    1、在组件之间复用状态逻辑很难(比如:render、props和高阶组件)
    2、复杂组件变得难以理解(比如:不想干的逻辑放在一起)
    3、难以理解的class(比如:this的工作方式)

    常见hook

    在这里插入图片描述

    如何实现useReducer

    用法:略

    注册Hook流程

    在这里插入图片描述

    ReactFiberHooks

    // 调度更新的方法
    // import { scheduleUpdateOnFiber } from 'ReactFiberWorkLoop';
    
    // ReactCurrentDispatcher是暴露给用户调用hook的接口,兼容mount和update时,调用相同hook,实现不同逻辑。(面向接口编程)
    /**
     * 
     interface IReactCurrentDispatcherValue {
         useReducer: <T>(reducer: (state: T, action: any) => T, initialState?: T) => [state: T, dispatch: (action: any) => void]
         useState: ...
     }
    */
    let ReactCurrentDispatcher = {
        current: null
    }
    
    // 当前渲染的Fiber
    let currentlyRenderingFiber = null;
    // 当前工作的hook
    let workInProgressHook = null;
    // 老hook
    let currentHook = null;
    
    // 初次渲染时🐶
    const HooksDispatcherOnMount = {
        useReducer: mountReducer
    }
    
    // 更新时的🐶
    const HooksDispatcherOnUpdate = {
        useReducer: updateReducer
    }
    
    // 暴露用户使用的useReducer
    export function useReducer(reducer, initialArg) {
        return ReactCurrentDispatcher.current.useReducer(reducer, initialArg);
    }
    
    // 顾名思义,调用函数组件返回children的方法,只不过带上了hook
    // current 老fiber
    // workInProgress 新fiber(当前执行的fiber)
    // Component
    export function renderWithHooks(_current, workInProgress, Component) {
        // 当前render的fiber
        currentlyRenderingFiber = workInProgress;
        // 清空fiber的memoizedState状态,这个是在fiber上用来保存函数组件hook的属性,它是链表结构
        workInProgress.memoizedState = null;
        // 根据是否存在老fiber判断是否是初次渲染
        if (_current !== null) {
                // 更新逻辑
                ReactCurrentDispatcher.current = HooksDispatcherOnUpdate;
        } else {
                // 加载逻辑
                ReactCurrentDispatcher.current = HooksDispatcherOnMount;
        }
        // 这里就是注册hook,同时返回函数组件的children
        let children = Component();
        // 清空数据
        currentlyRenderingFiber = null;
        currentHook = null;
        workInProgressHook = null;
        return children;
    }
    
    // 更新Reducer
    // 更新是不需要初始值的
    function updateReducer(reducer) {
        // 构建更新hook,并将起添加hooks链表中
        const hook = updateWorkInProgressHook();
        const queue = hook.queue;
        // 将reducer重新赋值给queue中
        // 想一想reducer此时会存在什么问题???
        queue.lastRenderedReducer = reducer;
        // 将老hook
        const current = currentHook;
        // pending就是我们dispatch时将更新状态储存在链表结构中
        const pendingQueue = queue.pending;
        // 开始实现更新状态
        if (pendingQueue !== null) {
            // 先拿到更新链表中的第一数据
            const first = pendingQueue.next;
            // 之前的state
            let newState = current.memoizedState;
            let update = first;
            // 如果不存在更新的数据或者循环到头了结束
            do {
                const action = update.action;
                newState = reducer(newState, action);
                update = update.next;
            } while (update !== null &amp;&amp; update !== first);
            // 清空更新队列
            queue.pending = null;
            // 更新state
            hook.memoizedState = newState;
            // 更新队列上的上一次state
            queue.lastRenderedState = newState;
        }
        const dispatch = dispatchAction.bind(null,currentlyRenderingFiber,queue);
        return [hook.memoizedState, dispatch];
    }
    
    // 当前工作的hook
    function updateWorkInProgressHook() {
        // 更新逻辑需要从老fiber的memoizedState去获取hook链表
        // 如果nextCurrentHook不存在,说明第一hook,从老fiber上拿到
        // 如果存在就可以从nextCurrentHook的next中拿到
        let nextCurrentHook;
        if (currentHook === null) {
            const current = currentlyRenderingFiber.alternate;
            nextCurrentHook = current.memoizedState;
        } else {
            nextCurrentHook = currentHook.next;
        }
        // 老fiber的hook
        currentHook = nextCurrentHook;
        // 重用hook逻辑
        const newHook = {
            // memoizedState保存的时上次的state
            memoizedState: currentHook.memoizedState,
            // 老fiber的更新队列
            queue: currentHook.queue,
            next: null,
        };
        // 通过重用老fiber的hook逻辑,将其用于构建新hook
        // 如果不存在工作的hook,那么currentlyRenderingFiber.memoizedState 和 workInProgressHook都需要更新
        // 否则通过next将其连接起来
        if (workInProgressHook === null) {
            currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
        } else {
            workInProgressHook = workInProgressHook.next = newHook;
        }
        // 返回的当前工作的hook
        return workInProgressHook;
    
    }
    export function mountReducer(reducer, initialArg) {
        // 构建hook以及添加hook链表中
        const hook = mountWorkInProgressHook();
        // 初始化状态
        let initialState = initialArg;
        hook.memoizedState = initialState;
        // 构建reducer的更新队列
        // lastRenderedReducer上一次的reducer
        // lastRenderedState 上一次状态
        const queue = (hook.queue = {pending: null, lastRenderedReducer: reducer, lastRenderedState: initialState});
        // dispatch 绑定了当前fiber和更新队列
        const dispatch = dispatchAction.bind(null,currentlyRenderingFiber,queue);
        // 返回state和更新方法
        return [hook.memoizedState, dispatch];
    }
    
    // 加载时创建hook及构建hook关系的方法
    export function mountWorkInProgressHook() {
        // hook
        // memoizedState记忆state值
        // queue 更新state的队列,我们可以调用很多次dispatch,那么这个值就会存在这个队列中的queue.pending上。
        // next 下一个hook
    
        // e.g
        /**
         {
             action: {type: 'ADD', payload: {number: 1}},
             next: null
         } 
        */ 
        const hook = {
            memoizedState: null,
            queue: null,
            next: null,
        };
    
        // 构建hook关系
        if (workInProgressHook === null) {
            currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
        } else {
            workInProgressHook = workInProgressHook.next = hook;
        }
        return workInProgressHook;
    }
    
    export function dispatchAction(fiber, queue, action) {
      // action 需要更新的状态
      const update = { action, next: null };
      const pending = queue.pending;
      // 在queue上构建链表
      if (pending === null) {
          update.next = update;
      } else {
          update.next = pending.next;
          pending.next = update;
      }
      queue.pending = update;
      // 对比新状态是否和老状态相同,决定是否更新
      const lastRenderedReducer = queue.lastRenderedReducer;
      const currentState = queue.lastRenderedState;
      const eagerState = lastRenderedReducer(currentState, action);
      if (Object.is(eagerState, currentState)) {
          return
      }
      // 将其fiber放到更新队列上
      scheduleUpdateOnFiber(fiber);
    }
    
    • 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
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201

    useState

    思考一下useState和useReducer是不是有相同的点?那怎么实现呐。。。

    期待更多。。。

  • 相关阅读:
    力扣两数之和
    长安旗下阿维塔科技增资扩股落定:宁德时代将持股约24%!
    加拿大CCPSA-SOR/2016-152(婴儿床、摇篮和婴儿摇篮法规)认证要求解答
    Unity 灯光组件Light
    vscode提效神器,中文一键转英文并生成多种命名格式供选择
    spring boot中的标注@Component、@Service等
    【用VSCode编写MarkDown并导出Epub电子书】
    「大数据-2.0」安装Hadoop和部署HDFS集群
    【GIT版本控制】--提交更改
    Web 应用程序安全测试指南
  • 原文地址:https://blog.csdn.net/yhyc812/article/details/125527218