【问题标题】:Weird behavior of useEffect in React.jsReact.js 中 useEffect 的奇怪行为
【发布时间】:2020-09-25 15:29:53
【问题描述】:

我在我的 React 应用程序中遇到了一些奇怪的行为,其中带有状态(比如 state1)作为第二个参数的 setEffect 正在更改另一个变量(不是状态)而没有调用 setState1。所以我编写了以下代码,看看是否会发生同样的情况,它确实发生了:

import React, { useState, useEffect } from 'react';

function App() {

  let [state1, setState1] = useState('');
  let count = 0;

  useEffect(() => {
    count = count + 1000;
    console.log('USEEFFECT_count: ' + count);
  }, [state1]);

  setInterval(() => {
    console.log('SETINTERVAL_count: ' + count);
  }, 1000);

  return (
    <div className="App">
    </div>
  );
}

export default App;

基本上,只有在调用 setState1 来更改 state1 的值时,变量 count 才应该收到 1000 的值。但是控制台的返回是

SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
 

正如预期的那样,useEffect 中的 console.log 永远不会被调用。但如果是这样的话,那么:

  • 他为什么要把 1000 相加?
  • 为什么这个值会重置为 0?

如果我添加一个 state2 和另一个总和为 5000 的 useEffect ,它会从第一个 useEffect 增加到 1000:

import React, { useState, useEffect } from 'react';

function App() {

  let [state1, setState1] = useState('');
  let [state2, setState2] = useState('');
  let count = 0;

  useEffect(() => {
    count = count + 1000;
    console.log('USEEFFECT1_count: ' + count);
  }, [state1]);

  useEffect(() => {
    count = count + 5000;
    console.log('USEEFFECT2_count: ' + count);
  }, [state2]);

  setInterval(() => {
    console.log('SETINTERVAL_count: ' + count);
  }, 1000);

  return (
    <div className="App">
    </div>
  );
}

export default App;

console.log 的结果:

SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    他们的代码输出结果并不奇怪。

    在您的代码中,useEffect 挂钩只会执行一次,即在初始渲染之后。然后它只会在 state1 被更改时执行,但在你的代码中永远不会发生,所以 useEffect 只执行一次并将 count 设置为 1000

    正如预期的那样,useEffect 中的 console.log 永远不会被调用

    不,它会被调用。 useEffect 将在初始渲染后执行,这就是为什么count 的值变为1000。在 useEffect 钩子的依赖数组中包含 state1 并不意味着它只会在 state1 更改时执行。 useEffect 始终在初始渲染后执行,无论您是否传递依赖数组或是否传递非空依赖数组

    现在,为什么setInterval 会记录count 的两个值?

    那是因为您的代码中设置了两个间隔。一次在初始渲染期间,count0,第二次是因为 Strict Mode 会渲染您的组件两次。

    由于setInterval() 在组件内的顶层调用,因此每当组件重新渲染时都会设置一个新的时间间隔。当您的组件渲染两次时,设置了两个间隔。定义回调函数时,每个区间的回调函数对count都有一个闭包。

    index.js文件中,你会看到App组件包裹在React.StrictMode中,如下图:

    <React.StrictMode>
      <App/>
    </React.StrictMode>
    

    如果您删除 React.StrictMode 组件,您将看到只有一个 count 的值将记录到控制台

    没有严格模式的输出:

    USEEFFECT_count: 1000 
    SETINTERVAL_count: 1000 
    SETINTERVAL_count: 1000 
    SETINTERVAL_count: 1000 
    SETINTERVAL_count: 1000 
    

    【讨论】:

      【解决方案2】:

      您对setInterval 的调用在组件的主体中,因此每次组件呈现时都会发生这种情况。您的组件正在渲染两次,因此它设置了两个间隔。每个间隔都引用不同的count 变量。 counts 之一是 1000,因为对应于该渲染的 useEffect 已经运行。另一个是 0,因为它的 useEffect 没有运行(因为 state1 没有改变)。

      有两个渲染的原因可能是因为您使用的是react strict mode,它故意进行双重渲染。这样做是为了帮助突出显示代码依赖于组件呈现次数的错误。所以它似乎在做它的工作,因为它突出了你对渲染数量的依赖。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-08-12
        • 2016-04-30
        • 1970-01-01
        • 2020-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-03
        相关资源
        最近更新 更多