Signal ledger / designed reading stage
Hooks 源码:useState, useReducer
useState, useReducer 和它的一切。
Transition band / reading fieldLong-form note2021-03-18
Continue through the ledger
useState, useReducer 和它的一切。
你可以在 useState 和
useReducer 中看到 useState 和
useReducer 的说明。
本文不对文档已有的基本内容做说明,你可以访问官网查看官方的说明。
useState 可以视为 useReducer 的语法糖,本质上都是返回了一个被储存的值,以及一个修改它的方法。
const [count, setCount] = useState(0)
// 类似
const [count, dispatchCount] = useReducer(countReducer, 0)
在后面的源码分析中也可以看到, useState 相对于 useReducer 来说,是赋值了一个默认的 reducer
方法。
通过 useState 以及 useReducer 返回的 setState 方法,可以设置 state
的值,并触发刷新。在其他的 hooks 中, useRef 也可以起到储存值的作用,但不同的是, useRef
不会触发页面的重新渲染。
在我们对 hooks 的解读中,解释了 hooks 的状态是如何记录并获取的。
在看这部分源码前,我们带着几个问题:
useState 是 useReducer 的语法糖?setState 方法更新值才会触发渲染?function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<
BasicStateAction<S>,
> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
function mountReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
const hook = mountWorkInProgressHook();
let initialState;
if (init !== undefined) {
initialState = init(initialArg);
} else {
initialState = ((initialArg: any): S);
}
hook.memoizedState = hook.baseState = initialState;
const queue = (hook.queue = {
pending: null,
interleaved: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
null,
currentlyRenderingFiber,
queue,
): any));
return [hook.memoizedState, dispatch];
}
对比 mountState 和 mountReducer 可以看到,区别仅仅是 计算 initialState 和 reducer。根据
useState
的文档,它的参数是一个值或者方法,在源码的处理中,遇到值则直接返回,遇到方法则返回执行结果。
对于“使用 useReducer 实现 useState”这类问题,可以给出如下答案:
function useMyState(initialState) {
const reducer = ((state, action) = typeof action === 'function' ? action(state) : action)
return useReducer(reducer, initialState)
}
function updateState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
return updateReducer(basicStateReducer, (initialState: any));
}
function updateReducer<S, I, A>(
reducer: (S, A) => S,
initialArg: I,
init?: I => S,
): [S, Dispatch<A>] {
const hook = updateWorkInProgressHook();
const queue = hook.queue;
queue.lastRenderedReducer = reducer;
// 省略
const dispatch: Dispatch<A> = (queue.dispatch: any);
return [hook.memoizedState, dispatch];
}
先忽略对于有未处理的更新队列这种情况,对于 useState 和 useReducer 来说,两者都是获取了已经存在的
hook.memoizedState 和 dispatch 并返回。