useState
上节讨论了fiber, 今天我们继续来看hook
function App() {
const [num, updateNum] = useState(0);
function increment() {
setTimeout(() => {
updateNum(num + 1);
}, 1000);
}
return <p onClick={increment}>{num}</p>;
首先提问: useState怎么保存状态的呢, 然后有时怎么更新状态的呢
带着问题我们继续往下思考
我们都知道useState改变会触发组件重新渲染, useRef值不会触发
多个hooks的连接指向
A-> B-> C
const hookA = {
// hook保存的数据
memoizedState: null,
// 指向下一个hook
next: hookB
// ...省略其他字段
// 本次更新以baseState为基础计算新的state
baseState: null,
// 本次更新开始时已有的update队列
baseQueue: null,
// 本次更新需要增加的update队列
queue: null,
};
hookB.next = hookC;
currentlyRenderingFiber.memoizedState = hookA;
currentlyRenderingFiber.memoizedState = hookA;
其中memoizedState存储这当前的值得状态
对于更新:
useState存在第二个参数: setXX
每次调用的时候都会产生一个update对象:
updateA -> updateB -> updateC
const update = {
// 更新的数据
action: action,
// 指向下一个更新
next: null
};
那么这个链表存储在哪里呢, 由上图可以知道存在 queue
,
顺序依次为 queue挂载在 baseQueue 然后与 baseState进行比对更新, 最后将更新结果赋予 memoizeState
再回头来看: , 多次点击的num+1, 由于还未来的及更新此时baseState依旧为0 , 所以最终渲染依旧是同一个值1
传函数与传值的区别
在计算state时,会将queue的环状链表剪开挂载在baseQueue最后面,
baseQueue基于baseState计算新的state。
let newState = baseState;
let firstUpdate = hook.baseQueue.next;
let update = firstUpdate;
// 遍历baseQueue中的每一个update
do {
if (typeof update.action === 'function') {
newState = update.action(newState);
} else {
newState = action;
}
} while (update !== firstUpdate)
如果其中setTimeout 的时间改的很小, 比如直接不设置
function increment() {
setTimeout(() => {
updateNum(num + 1);
});
}
如果会发生改变么?
那如果我们使用useReducer 呢????