我有以下代码(反应16.8.6,react-dom:16.8.6):
import React, { useState, useEffect, } from 'react';
import ReactDOM from 'react-dom';
function App(props) {
const [counter, setCounter] = useState(0);
console.log(1)
useEffect(() => {
console.log(2)
setTimeout(() => {
console.log(3)
setCounter(1);
}, 10000)
});
return counter;
}
ReactDOM.render(<App />, document.getElementById('root'));
第一次渲染App
组件时,它会打印:
1 2
10秒后它将打印:
3,1,2
以前的一切对我来说都是可以理解的,但是现在10秒钟后它可以打印
3,1
即没有调用传递给useEffect的函数。我认为它以某种方式与状态相关(因为它不会改变,如果改变,useEffect可以正常工作)。您能解释一下这种行为吗?
答案 0 :(得分:4)
根据docs
useEffect是否在每次渲染后运行?是!默认情况下,它在第一次渲染后和每次更新后都运行。 (我们将在后面讨论如何自定义此功能。)您可能会发现更容易想到效果发生在“渲染后”,而不是“安装”和“更新”。 React保证DOM在运行效果时已被更新。
第二次调用setCounter
之后,从未发生更新,因为1 === 1
始终相同。
如果每次您实际上将计数器增加一,您将获得理想的效果。
function App(props) {
const [counter, setCounter] = useState(0);
console.log(1)
useEffect(() => {
console.log(2)
setTimeout(() => {
console.log(3)
setCounter(counter + 1);
}, 10000)
});
return counter;
}
答案 1 :(得分:1)
每次渲染组件时,都会调用useEffect,因为您没有提供数组作为第二个参数。
如果您阅读docs,将会看到,如果不提供第二个参数,则每次都会调用它。
如果提供一个空数组,则只会在第一个渲染器上调用它。
如果您在数组中提供了一个变量,则只有在该变量发生更改时才会执行该变量。
3,1之所以会被调用,是因为在调用2后,超时将被重新设置并打印3。用setCounter(1);
更新状态后,组件将重新渲染,并且1将被再次调用。
希望这会有所帮助。编码愉快。
答案 2 :(得分:1)
如果将状态挂钩更新为与当前状态相同的值,React将纾困,而不会渲染子代或发射效果。 (React使用Object.is比较算法。)
请注意,React可能仍需要再次渲染该特定组件,然后才能发布。不必担心,因为React不会不必要地“深入”到树中。如果渲染时要进行昂贵的计算,则可以使用useMemo对其进行优化。
这就是这里发生的情况:组件重新渲染,但是由于当前状态为1,并且在第二次超时后将其设置为1,React会“释放”,因此不会重新触发效果。
旁注:效果实际上应该仅取决于counter
,而不通常取决于更新。