问题
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