3

Why is the first functional component slower than the second functional component when they are composed side by side in a React application and you switch tabs and then go back to it after a few seconds?

Here is a sandbox so you can see it in action.

https://codesandbox.io/s/useeffect-87pm7

function SlowerCounter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => clearInterval(intervalId);
  }, [count]);

  return <div>The count is: {count}</div>;
}
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(count => ++count);
    }, 1000);

    return () => clearInterval(intervalId);
  }, []);

  return <div>The count is: {count}</div>;
}
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
Jeremy Levett
  • 939
  • 9
  • 13
  • 1
    The dependency array to your second `useEffect` includes `count` which means that the whole effect setup/teardown is run every time it changes. After each `setCount` you `setInterval` and `clearInterval`. – Dan Prince Nov 13 '19 at 11:00

2 Answers2

2

The problem is that in the second useEffect you set and clear a new interval on every render, while others keep running on the same instance.

It causes a different effect on the interval when you switching tabs, therefore, the useEffect logic and the understanding of how browser tabs work causes the "bug".

Try adding logging for every clearing function in useEffect:

function SuggestedWayToUseEffectOneButItsActuallyNotWorkingCorrectly() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(count + 1);
    }, 1000);

    return () => {
      console.log('cleared 2');
      clearInterval(intervalId);
    };
  }, [count]);

  return <div>The count is: {count}</div>;
}

Edit useEffect

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
0

When you pass second parameter with an empty array in the useEffect it doesn't need to check on every render. It will continue updating from previous value. But when you pass the parameter with a lookup value in an array, then it will need to check on every render and app will continue updating from previous value.

So, when switching to different tabs back and forth the app will unmount and re-mount and the calculation between them you find a little bit slower as it checks for the value after rendering but with an empty array will continue updating without any check. So, the time gap between useEffect cached value check is what you see is slower.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231