如果状态不变,为什么useEffect不调用回调函数?

时间:2019-08-12 17:40:16

标签: javascript reactjs react-hooks

我有以下代码(反应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可以正常工作)。您能解释一下这种行为吗?

3 个答案:

答案 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;
}

Live example

答案 1 :(得分:1)

每次渲染组件时,都会调用useEffect,因为您没有提供数组作为第二个参数。

如果您阅读docs,将会看到,如果不提供第二个参数,则每次都会调用它。

如果提供一个空数组,则只会在第一个渲染器上调用它。

如果您在数组中提供了一个变量,则只有在该变量发生更改时才会执行该变量。

3,1之所以会被调用,是因为在调用2后,超时将被重新设置并打印3。用setCounter(1);更新状态后,组件将重新渲染,并且1将被再次调用。

希望这会有所帮助。编码愉快。

答案 2 :(得分:1)

React docs:

  

如果将状态挂钩更新为与当前状态相同的值,React将纾困,而不会渲染子代或发射效果。 (React使用Object.is比较算法。)

     

请注意,React可能仍需要再次渲染该特定组件,然后才能发布。不必担心,因为React不会不必要地“深入”到树中。如果渲染时要进行昂贵的计算,则可以使用useMemo对其进行优化。

这就是这里发生的情况:组件重新渲染,但是由于当前状态为1,并且在第二次超时后将其设置为1,React会“释放”,因此不会重新触发效果。


旁注:效果实际上应该仅取决于counter,而不通常取决于更新。