Search code examples
reactjsreact-hooksuse-effect

React UseEffect hook Issue Rendering an Interval


In the following React Component below, I am trying to add increment count by each second passed so it looks like a stopwatch, but the count is shown as 2, then blinks to 3, and back to 2. Does anyone know how to deal with this bug, and get the count to show up as intended?

import React, { useEffect, useState } from "react";

const IntervalHook = () => {
  const [count, setCount] = useState(0);

  const tick = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    const interval = setInterval(tick, 1000);

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

  return <h1> {count} </h1>;
};

export default IntervalHook;

Solution

  • if you want to change some state based on its previous value, use a function:setCount(count => count + 1); and your useEffect becomes independant of [ count ]. Like

    useEffect(() => {
      const tick = () => {
        setCount(count => count + 1);
      };
    
      const interval = setInterval(tick, 1000);
    
      return () => {
        clearInterval(interval);
      };
    }, [setCount]);
    

    or you get rid of tick() and write.

    useEffect(() => {
      const interval = setInterval(setCount, 1000, count => count + 1);
    
      return () => {
        clearInterval(interval);
      };
    }, [setCount]);
    

    But imo it's cleaner to use a reducer:

    const [count, increment] = useReducer(count => count + 1, 0);
    
    useEffect(() => {
      const interval = setInterval(increment, 1000);
    
      return () => {
        clearInterval(interval);
      };
    }, [increment]);