We made a new video on Shadcn UI. Check it out!
React Hooks: useEffect - 1

Solution

Fix the useEffect bug

GreatFrontEnd has better questions!

We don't create questions because GreatFrontEnd already has the best possible library of questions and we recommend the same to our community.

affiliate

The bug is evident whenever we reset the timer. But it also can be noticed when the timer is left to run for a few seconds.

The seconds state variable is set inside a setInterval which itself is inside a useEffect.

The current useEffect runs every time the seconds state variable changes and the setInterval is not being cleared resulting in a memory leak.

We can fix this bug with a simple useEffect cleanup function.

App.jsx
React.useEffect(() => {
  const interval = setInterval(() => {
    setSeconds(seconds + 1);
  }, 1000);
 
  return () => {
    clearInterval(interval);
  };
}, [seconds]);

Did you know that instead of clearInterval you can also use clearTimeout? Both of them share the same pool of IDs. But for readability, we should use the respective clear functions.

Though the bug is fixed, we prefer a different way to set the state which allows us to not pass the seconds value to the useEffect dependency array. setSeconds can take an updater function

App.jsx
React.useEffect(() => {
  const interval = setInterval(() => {
    setSeconds(seconds + 1);
    setSeconds(seconds => seconds + 1);
  }, 1000);
 
  return () => {
    clearInterval(interval);
  };
}, [seconds]);
}, []);

Bonus

Creating a useTimer hook is a great way to abstract away the timer logic.

useTimer.jsx
import React from 'react';
 
export default function useTimer() {
  const [seconds, setSeconds] = React.useState(0);
 
  React.useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((seconds) => seconds + 1);
    }, 1000);
 
    return () => {
      clearInterval(interval);
    };
  }, []);
 
  const resetTimer = () => {
    setSeconds(0);
  };
 
  return { seconds, resetTimer };
}

All we had to do was extract the useState and useEffect logic into the useTimer hook. The useTimer hook returns an object containing the seconds state and a resetTimer function which we use back in the App component.

App.jsx
import useTimer from './useTimer';
 
export default function App() {
  const [seconds, setSeconds] = React.useState(0);
 
  React.useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((seconds) => seconds + 1);
    }, 1000);
 
    return () => {
      clearInterval(interval);
    };
  }, []);
  const { seconds, resetTimer } = useTimer();
 
  return (
    <div className="p-4 text-center">
      <p>Timer: {seconds} Seconds</p>
      <button
        className="rounded bg-red-500 p-2 hover:bg-red-600"
        onClick={() => setSeconds(0)}
        onClick={resetTimer}
      >
        Reset Timer
      </button>
    </div>
  );
}

GreatFrontEnd has better questions!

We don't create questions because GreatFrontEnd already has the best possible library of questions and we recommend the same to our community.

affiliate

Last updated on

On this page