当使用 setInterval 时,React 渲染而不实际更改状态

IT技术 reactjs render setinterval use-state react-state
2021-05-15 13:31:42

我将分两步介绍我的问题,两者的代码块略有不同。

步骤1:

下面我们有一个 React 应用程序,它每两秒渲染一次,因此导致浏览器将渲染打印到控制台。如果用户按下任意键,渲染将停止,从而停止控制台打印。请暂时忽略注释掉的行。

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

function App() {

  const [number, setUpdate] = useState(0);
  const [isPaused, setIsPaused] = useState(false);

  const intervalRef = useRef(undefined);

  useEffect(() => {
    intervalRef.current = setInterval(() => setUpdate(prevNumber => ++prevNumber), 2000);
    window.addEventListener('keydown', handleKeyDown);
  }, []);

  const handleKeyDown = () => {
    clearInterval(intervalRef.current);
    console.log('console log here');

    // setIsPaused(isPaused);
  };

  console.log('render');

  return null;
};

export default App;

这是应用程序的屏幕截图: 在此处输入图片说明

上面发生的事情是,我让组件渲染了五次,然后我按下了一个键来停止组件渲染。

第2步:

在第 2 步中,我们有完全相同的应用程序,只是没有注释掉handleKeyDown 中设置的状态

  const handleKeyDown = () => {
    clearInterval(intervalRef.current);
    console.log('console log here');

    // This is no longer commented out. Why does it cause a new render?
    setIsPaused(isPaused);
  };

这是应用程序的屏幕截图,其中包含在步骤 2 中进行的代码更改: 在此处输入图片说明

同样,在我按下一个键后,我让组件渲染了五次。但是现在有一个额外的渲染,即使状态不应该改变(因为状态实际上并没有改变,因为我们设置了与状态中已经存在的值相同的值)setIsPaused(isPaused)

我很难理解在第 2 步导致额外渲染的原因可能是什么。也许这很明显?

如果我注释掉由 setInterval 运行的其他状态更改,则setIsPaused(isPaused)永远不会导致新的渲染,这让我更加困惑。

2个回答

这是一个已知的怪癖,请参阅#17474这是新的并发模式引入的副作用。组件函数确实重新运行了,但 DOM 将保持不变。

我还发现人们发布了这个有趣的例子你可以用代码试试 exp 。组件函数包含一些类似的东西,<div>{Math.random()}</div>虽然随机数在函数的额外重新运行中确实发生了变化,但如果状态没有改变,它就不会反映到 DOM 上。

结论。大多数情况下,您可以认为这种副作用是无害的。

更新的状态从未手段DOM将重新呈现,̶你是对̶ ̶s̶e̶t̶I̶s̶P̶a̶u̶s̶e̶d̶(̶i̶s̶P̶a̶u̶s̶e̶d̶)̶̶不会重新呈现DOM中,̶但将设置状态,并给你更新̶v̶a̶l̶u̶e̶.̶̶(我正在考虑唯一的状态与现场̶ ̶i̶s̶P̶a̶u̶s̶e̶d̶̶没有别的)̶

更新:在阅读接受的答案之前,我确实知道这种行为存在。