当使用 setInterval 时,React 渲染而不实际更改状态 [英] React renders without actually changing the state when setInterval is used

查看:33
本文介绍了当使用 setInterval 时,React 渲染而不实际更改状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm going to introduce my question in two steps with slightly different code blocks in both.

第 1 步:

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

Below we have a React application which renders itself every two seconds and therefore causes the browser to print render to the console. If the user presses any key, the renders will stop which in turn stops the console prints. Please ignore the line commented out for now.

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;

这是应用程序的屏幕截图:

Here is a screenshot of the application:

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

What has happened above, is that I've let the component render five times and then I've pressed a key to stop the component from rendering.

第 2 步:

在第 2 步中,除了不注释掉handleKeyDown 中设置的状态之外,我们有完全相同的应用程序.

In step 2 we have exactly the same application with the exception of not commenting out the state set in 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 步中进行的代码更改:

Here is a screenshot of the application with the code change made in step 2:

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

Again, I've let the component to render five times after I've pressed a key. But now there is an extra render even though the state should not be changing (because the state is not actually mutating because we set the same value as was already in the state) by setIsPaused(isPaused).

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

I having difficulty to understand what might the reason to cause the extra render at step 2. Maybe it's something obvious?

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

setIsPaused(isPaused) never causes a new render if I comment out the other state change which is run by setInterval which makes me even more baffled.

推荐答案

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

This is a known quirk, see #17474. It’s a side effect introduced by the new concurrent mode. The component function did re-run, but DOM will remain untampered.

我还发现有人发布了这个有趣的例子.你可以用代码试试 exp .组件函数包含类似 <div>{Math.random()}</div> 的内容,尽管随机数在函数的额外重新运行中确实发生了变化,但它不会反映到如果状态未更改,则为 DOM.

I also found people post this interesting example. You can try exp with the code. The component function contains something like <div>{Math.random()}</div> although random number did changed in that extra re-run of the function, it wouldn’t reflect onto DOM if state isn’t changed.

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

Conclusion. You can consider this side effect harmless most of time.

这篇关于当使用 setInterval 时,React 渲染而不实际更改状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆