React renders without actually changing the state when setInterval is used

大憨熊 提交于 2021-01-28 11:36:10

问题


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

Step 1:

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.

Step 2:

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);
  };

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

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).

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

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.


回答1:


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.

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.




回答2:


U̶p̶d̶a̶t̶i̶n̶g̶ ̶a̶ ̶s̶t̶a̶t̶e̶ ̶n̶e̶v̶e̶r̶ ̶m̶e̶a̶n̶s̶ ̶D̶O̶M̶ ̶w̶i̶l̶l̶ ̶r̶e̶r̶e̶n̶d̶e̶r̶,̶ ̶y̶o̶u̶ ̶a̶r̶e̶ ̶r̶i̶g̶h̶t̶ ̶̶s̶e̶t̶I̶s̶P̶a̶u̶s̶e̶d̶(̶i̶s̶P̶a̶u̶s̶e̶d̶)̶̶ ̶w̶i̶l̶l̶ ̶n̶o̶t̶ ̶r̶e̶r̶e̶n̶d̶e̶r̶ ̶t̶h̶e̶ ̶D̶O̶M̶,̶ ̶b̶u̶t̶ ̶i̶t̶ ̶w̶i̶l̶l̶ ̶s̶e̶t̶ ̶t̶h̶e̶ ̶s̶t̶a̶t̶e̶ ̶a̶n̶d̶ ̶w̶i̶l̶l̶ ̶g̶i̶v̶e̶ ̶y̶o̶u̶ ̶t̶h̶e̶ ̶u̶p̶d̶a̶t̶e̶d̶ ̶v̶a̶l̶u̶e̶.̶ ̶(̶I̶ ̶a̶m̶ ̶c̶o̶n̶s̶i̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶l̶y̶ ̶s̶t̶a̶t̶e̶ ̶w̶i̶t̶h̶ ̶f̶i̶e̶l̶d̶ ̶̶i̶s̶P̶a̶u̶s̶e̶d̶̶ ̶n̶o̶t̶h̶i̶n̶g̶ ̶e̶l̶s̶e̶)̶

Update: I did know this behavior existed until reading the accepted answer.



来源:https://stackoverflow.com/questions/64080311/react-renders-without-actually-changing-the-state-when-setinterval-is-used

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!