我试图弄清楚 useEffect 何时导致重新渲染。我对以下示例的结果感到非常惊讶:
https://codesandbox.io/embed/romantic-sun-j5i4m
function useCounter(arr = [1, 2, 3]) {
const [counter, setCount] = useState(0);
useEffect(() => {
for (const i of arr) {
setCount(i);
console.log(counter);
}
}, [arr]);
}
function App() {
useCounter();
console.log("render");
return <div className="App" />;
}
这个例子的结果如下:
我不知道为什么:
- 该组件仅渲染 3 次(我猜该组件会在每次调用
setCount
+ 一次初始渲染时重新渲染 - 所以 4 次) - 计数器只有两个值 0 和 3:我猜,正如 本文 所述,每个渲染都会看到自己的状态和道具,因此整个循环将以每个状态作为常数 (1, 2, 3) 运行–>但是为什么状态永远不是 2?
原文由 Xen_mar 发布,翻译遵循 CC BY-SA 4.0 许可协议
我将尽我所能解释(或介绍)正在发生的事情。我也在第 7 点和第 10 点做了两个假设。
useEffect
挂载后调用。useEffect
将“保存”初始状态,因此counter
在其中引用时将为 0。setCount
被调用来更新计数,控制台日志记录根据“存储”版本为 0 的计数器。因此数字 0 在控制台中记录了 3 次。因为状态已经改变 (0 -> 1, 1 -> 2, 2 -> 3) React 设置像一个标志或其他东西来告诉自己记住重新渲染。useEffect
执行期间没有重新渲染任何东西,而是等到useEffect
完成重新渲染。useEffect
完成,React 会记住counter
的状态在其执行期间发生了变化,因此它将重新渲染应用程序。useCounter
。注意这里没有参数传递给useCounter
自定义钩子。 假设: 我自己也不知道这一点,但我认为默认参数似乎是再次创建的,或者至少以某种方式让 React 认为它是新的。因此,因为arr
被视为新的,所以useEffect
挂钩将再次运行。这是我可以解释useEffect
第二次运行的唯一原因。useEffect
counter
的值将为 3。因此,控制台日志将按预期记录数字 3 三倍。useEffect
第二次运行后,React 发现计数器在执行期间发生了变化(3 -> 1, 1 -> 2, 2 -> 3),因此应用程序将重新渲染导致第三次“渲染”日志。useCounter
钩子的内部状态在此渲染和之前的渲染之间没有从应用程序的角度发生变化,它不会执行其中的代码,因此useEffect
不叫第三次。所以应用程序的第一次渲染总是会运行钩子代码。第二次App看到hook的内部状态把它的counter
从0变成了3,于是决定重新运行,第三次看到hook的内部状态还是3 3 所以它决定不重新运行它。这是我能想出的让钩子不再运行的最好理由。您可以在钩子本身中放置一个日志,以查看它实际上不会运行第三次。这就是我看到的情况,我希望这让它更清楚一点。