本文将带大家实现 useState。
先看下如何使用。
js
function FunctionComponent() {
const [count1, setCount1] = useState(1);
return (
<div className="border">
<button
onClick={() => {
setCount1(count1 + 1);
}}
>
{count1}
</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
(<FunctionComponent />) as any
);
useState 是基于 useReducer 实现的,参考文章 React 实现 useReducer。
实现 useState
js
// 源码中 useState 与 useReducer 对比:
// useState 如果 state 没有改变,不引起组件更新。useReducer 不是如此。(老的源码中,state 没有改变,两者都不更新)
// reducer 代表 state 修改规则,useReducer 比较方便复用这个规则
export function useState<S>(initialState: (() => S) | S) {
const init = isFn(initialState) ? (initialState as any)() : initialState;
return useReducer(null, init);
}
useReducer 修改
为了适配 useState
,useReducer
和 dispatchReducerAction
两个函数的 reducer 参数增加了 null。
js
export function useReducer<S, I, A>(
reducer: ((state: S, action: A) => S) | null,
initialArg: I,
init?: (initialArg: I) => S
) {
...
}
function dispatchReducerAction<S, I, A>(
fiber: Fiber,
hook: Hook,
reducer: ((state: S, action: A) => S) | null,
action: any
) {
...
}
再聊 scheduleUpdateOnFiber
Fiber hooks 调用 scheduleUpdateOnFiber
时,第三个参数传的都是 true。
因为 React 考虑到 ensureRootIsScheduled
使用到了调度器,会增加复杂度。
js
export function scheduleUpdateOnFiber(
root: FiberRoot,
fiber: Fiber,
isSync?: boolean
) {
workInProgressRoot = root;
workInProgress = fiber;
if (isSync) {
// queueMicrotask 是 JS 原生 API
queueMicrotask(() => performConcurrentWorkOnRoot(root));
} else {
ensureRootIsScheduled(root);
}
}
export function performConcurrentWorkOnRoot(root: FiberRoot) {
// 1. render, 构建 fiber 树,即 VDOM(beginWork|completeWork)
renderRootSync(root);
const finishedWork = root.current.alternate;
root.finishedWork = finishedWork; // 根 Fiber
// 2. commit, VDOM -> DOM
commitRoot(root);
}