在使用useState时发现了一个问题,子组件多次触发修改后,再点击父组件发现子组件的值出现了问题。
const Foo = () => {
const [count, setCount] = React.useState(10)
function handleClick() {
setCount((c) => c + 1)
}
return (
<div>
Foo: {count}
<button onClick={handleClick}>click1</button>
</div>
)
}
const App = () => {
const [num, setNum] = React.useState(12)
function handleClick() {
setNum((c) => c + 1)
}
return (
<div id='app'>
hello React: {num}
<button onClick={handleClick}>click</button>
<div>
<Foo></Foo>
</div>
</div>
)
}
如下图所示操作,得到的结果应该分别是 13,12
但实际得到的结果
从操作上来看,问题出现在第二次点击的时候,第二次点击的是<App />组件,此时的流程是这样的:
触发了<App />组件中的setState方法并将action收集了起来,然后赋值给nextUnitOfFier后触发了组件的更新。
组件在更新过程中,遇到了<Foo />组件并进行解析,并执行了<Foo />组件中的useState方法。
在<Foo />组件中的useState方法中,取到了oldHook,并进行了对stateHook进行赋值。
重点来了!
按照正常的逻辑,此时的stateHook的数据应该是
{state: 12, queue: []}
。但实际上我们通过断点调试发现是
{state: 10, queue: [f]}
。
所以后面的逻辑是:
stateHook.queue
方法并给 stateHooks.state
赋值并return回去了。(所以stateHooks.state = 11)function useState(initial) {
let currentFiber = wipFiber;
const oldHook = currentFiber.alternate?.stateHooks[stateHookIndex];
const stateHook = {
state: oldHook ? oldHook.state : initial,
queue: oldHook ? oldHook.queue : [],
};
stateHook.queue.forEach((action) => {
stateHook.state = action(stateHook.state);
});
stateHook.queue = [];
stateHooks.push(stateHook);
stateHookIndex++;
currentFiber.stateHooks = stateHooks;
function setState(action) {
let eagerState =
typeof action === "function" ? action(stateHook.state) : action;
if (eagerState === stateHook.state) {
return;
}
stateHook.queue.push(
typeof action !== "function" ? () => action : action
);
// 有问题的代码
wipRoot = {
...currentFiber,
alternate: currentFiber,
};
nextUnitOfFier = wipRoot;
}
return [stateHook.state, setState];
}
分析完逻辑后就找到了问题: