useState
基本用法
1 | import React, { useState } from 'react'; |
存储state
React.useState
1
2
3
4
5
6
7
8
9
10export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
function resolveDispatcher() {
var dispatcher = ReactCurrentDispatcher.current;
return dispatcher;
}dispatcher.useState(initialState)最终调用mountState
mountState
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function mountState(initialState) {
var hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
// $FlowFixMe: Flow doesn't like mixed types
initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;
var queue = hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: initialState
};
var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);
return [hook.memoizedState, dispatch];
}mountWorkInProgressHook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}state是链式存储在fiber节点上的memoizedState属性的
workInProgressHook = workInProgressHook.next = hook
这句代码实现了将state链式存储- 在实际调用的时候也是这样,每次执行useState会返回当前的state,然后把current指向next,从而实现每个useState都能拿到之前定义的state值
- 所以才规定,useState必须在顶层调用,不能在if语句中和循环中使用,因为他的获取值的顺序完全依赖执行顺序。
更新state
1 | // 返回的dispath是这么定义的 |
useState(re-render)
1
2HooksDispatcherOnUpdate.useState() -> return updateState(initialState)
return updateReducer(basicStateReducer);updateReducer
1
2
3
4
5
6
7
8
9
10function updateReducer(reducer, initialArg, init) {
var hook = updateWorkInProgressHook();
var queue = hook.queue;
var pendingQueue = queue.pending;
current.baseQueue = baseQueue = pendingQueue
action = currentHook.baseQueue.next.action
newState = reducer(newState, action)
hook.memoizedState = newState;
return [hook.memoizedState, dispatch]
}basicStateReducer
1
2
3
4function basicStateReducer(state, action) {
// $FlowFixMe: Flow doesn't like mixed types
return typeof action === 'function' ? action(state) : action;
}updateReducer
最终是使用了currentHook的queue的pending.next的action,这个值是在setState时更新的- 更新stage时只是把要更新的状态存储在队列中,最终re-render时会去执行reducer,然后拿到最新的state
- useState内部其实是使用了useReducer
- 每次执行useState,会执行resolveDispatcher从而获得当前的dispatcher,返回的是ReactCurrentDispatcher.current,这个值会根据运行情况进行变化,从而实现mount和update时,最终调用不同的方法,初始化时,是mountState,更新时是调用udateState
useEffect
基本用法
1 | import React, { useState, useEffect } from 'react'; |
源码
1 | function useEffect(create, deps) { |
- 最终是把create方法(useEffect时的第一个参数)放到effect对象中,effect对象又被推入currentlyRenderingFiber的updateQueue中
什么时候执行
1 | function commitHookEffectListMount(tag, finishedWork) { |
- 把执行create的结果赋值给effect的destroy属性,
deps是怎么起作用的
更新时执行以下代码
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
26function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps): void {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
pushEffect(hookEffectTag, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.effectTag |= fiberEffectTag;
hook.memoizedState = pushEffect(
HookHasEffect | hookEffectTag,
create,
destroy,
nextDeps,
);
}areHookInputsEqual
会比较上一次的deps和最新的deps是否相同,如果不想同,则执行pushEffect
useEffect返回的函数何时执行
1 | function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) { |
- 在执行unMount时,会把effect上的destroy取出来执行,而destroy又是执行effect时的结果