Skip to content
On this page

4. React18 - useReducer


示例

jsx
import * as React from 'react';
import { createRoot } from "react-dom/client";

function FunctionComponent() {
  const [number, setNumber] = React.useReducer((state, action) => {
    if (action.type == "add") {
      return state + action.payload;
    } else {
      return state;
    }
  }, 0);

  return <button onClick={() => {
    setNumber({ type: 'add', payload: 1 });//update1=>update2=>update1
    setNumber({ type: 'add', payload: 2 });//update2
  }}>{number}</button>
}

let element = <FunctionComponent />
const root = createRoot(document.getElementById("root"));
root.render(element);

实现原理

完整流程图:draw.io (diagrams.net)

总结

  • 初始挂载
    1. 初次渲染构建fiber树,建立ReactCurrentDispatcher.current与HooksDispatcherOnMount的关联
    2. 调用Function Component 执行React.useReducer(HooksDispatcherOnMount),直接拿reducer 的initial值
    3. jsx 通过bebal 转义Function Component 执行结果得到vdom,此时vdom 就引用hook 的初始值,然后会把初始值放置在vdom的props上
    4. 创建button fiber 会将vdom的props 赋值给fiber.pendingProps
    5. 在完成阶段会将,创建真实button dom 并用fiber.pendingProps设置其属性
  • 点击button按钮,触发调用HooksDispatcherOnMount.useReducer 绑定的dispatchReducerAction 方法,然后开启重新从root 更新
    • 构建新的Fiber树
    • 建立ReactCurrentDispatcher.current与HooksDispatcherOnUpdate 的关联
    • 调用FunctionComponent 执行React.useReducer(HooksDispatcherOnUpdate ),拿到newState 状态
    • jsx 通过bebal 转义Function Component 执行结果得到vdom,此时vdom 就引用hook 的newState 值,然后会把值放置在vdom的props上
    • 复用老的button Fiber 节点,将vdom props 放置button fiber.pendingProps 上面
    • 在完button fiber完成阶段,对比新老props 得到updatePayload 将其挂载至button fiber.updateQueue上面
  • 最后:在commitRoot 阶段,将button fiber.updateQueue 更新至真实Dom节点

常见疑问

  • 每种类型的节点memoizedState,分别都是存下什么
    • HostRootFilber.memoizedState:element vdom
    • FunctionComponent和其他Fiber:updateQueue
    • Hook.memoizedState: reducer 初始值
  • 哪里使用hook.memoizedState,然后给元素进行真实更新的呢?
    • 没并有用直接将hook.memoizedState 在哪里赋值给真实Dom 属性,而是重新通过jsx bebal 编译产生新的虚拟dom时使用了更新newState

Released under the MIT License.