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)
总结
- 初始挂载
- 初次渲染构建fiber树,建立ReactCurrentDispatcher.current与HooksDispatcherOnMount的关联
- 调用Function Component 执行React.useReducer(HooksDispatcherOnMount),直接拿reducer 的initial值
- jsx 通过bebal 转义Function Component 执行结果得到vdom,此时vdom 就引用hook 的初始值,然后会把初始值放置在vdom的props上
- 创建button fiber 会将vdom的props 赋值给fiber.pendingProps
- 在完成阶段会将,创建真实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