3

I have created a countdown timer component that passes the data to another component, and I use setTimeout. The component works as expected but an warning appears in my console:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

As I use setTimeout in useEffect I tried to add clearInterval in a return function, but this doesn't seem to help. Bellow you can find my component:

export default function createEvent(WrappedComponent) {
  const Event = props => {
    const [{ endDate }] = useState(props);
    const [days, setDays] = useState('');
    const [hours, setHours] = useState('');
    const [minutes, setMinutes] = useState('');
    const [seconds, setSeconds] = useState('');

    const interval = useRef();

    useEffect(() => {
      interval.current = setInterval(() => {
        const date = moment.unix(endDate).format('MM DD YYYY, h:mm a');
        const then = moment(date, 'MM DD YYYY, h:mm a');
        const now = moment();
        const countdown = moment(then - now);
        const daysFormat = countdown.format('D');
        const hoursFormat = countdown.format('HH');
        const minutesFormat = countdown.format('mm');
        const secondsFormat = countdown.format('ss');

        setDays(`${daysFormat} days`);
        setHours(hoursFormat);
        setMinutes(minutesFormat);
        setSeconds(secondsFormat);

        return () => {
          clearInterval(interval.current);
          interval.current = null;
        };
      }, 1000);
    }, []);

    return (
      <WrappedComponent
        days={days}
        hours={hours}
        minutes={minutes}
        seconds={seconds}
        {...props}
      />
    );
  };

  return Event;
}

If I try to imitate clear from somewhere else place by adding another useEffect right under the last one, like so:

  useEffect(() => {
    setTimeout(() => clearInterval(interval.current), 15000)
  }, [])

Warning disappears but the countdown will not working any more. So how do I make this right, to not affect the countdown and clear the warning?

0

4 Answers 4

2

The clean up function for a useEffect hook [1] needs to be returned from the function you give to useEffect. You are returning your clean up function in the function you give to setInterval.

useEffect(() => {
  interval.current = setInterval(() => {
    const date = moment.unix(endDate).format('MM DD YYYY, h:mm a');
    const then = moment(date, 'MM DD YYYY, h:mm a');
    const now = moment();
    const countdown = moment(then - now);
    const daysFormat = countdown.format('D');
    const hoursFormat = countdown.format('HH');
    const minutesFormat = countdown.format('mm');
    const secondsFormat = countdown.format('ss');

    setDays(`${daysFormat} days`);
    setHours(hoursFormat);
    setMinutes(minutesFormat);
    setSeconds(secondsFormat);
  }, 1000);

  // move clean up function to here:
  return () => {
    clearInterval(interval.current);
    interval.current = null;
  };
}, []);

[1] https://reactjs.org/docs/hooks-effect.html

Sign up to request clarification or add additional context in comments.

1 Comment

Omg, how I did not noticed that... )) crazy me... Thanks for noticing @sdgluck
1

You don't need createRef for effects. and return should be after the interval.

useEffect(() => {
      const timer = setInterval(() => {
        const date = moment.unix(endDate).format('MM DD YYYY, h:mm a');
        const then = moment(date, 'MM DD YYYY, h:mm a');
        const now = moment();
        const countdown = moment(then - now);
        const daysFormat = countdown.format('D');
        const hoursFormat = countdown.format('HH');
        const minutesFormat = countdown.format('mm');
        const secondsFormat = countdown.format('ss');

        setDays(`${daysFormat} days`);
        setHours(hoursFormat);
        setMinutes(minutesFormat);
        setSeconds(secondsFormat);

      }, 1000);

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

Comments

1

I use https://usehooks-ts.com/react-hook/use-interval

npm i usehooks-ts

Sample code in the url. Stable use of memory.

Comments

0
useEffect(() => {
      interval.current = setInterval(() => {
        const date = moment.unix(endDate).format('MM DD YYYY, h:mm a');
        const then = moment(date, 'MM DD YYYY, h:mm a');
        const now = moment();
        const countdown = moment(then - now);
        const daysFormat = countdown.format('D');
        const hoursFormat = countdown.format('HH');
        const minutesFormat = countdown.format('mm');
        const secondsFormat = countdown.format('ss');

        setDays(`${daysFormat} days`);
        setHours(hoursFormat);
        setMinutes(minutesFormat);
        setSeconds(secondsFormat);
      }, 1000);

  // Attention : return function cleanup for the function used in useEffect()
  return () => {
      clearInterval(interval.current);
      interval.current = null;
    };
}, []);

You could view the my full answer "Cleanup memory leaks on an Unmounted Component in React Hooks" right here : https://stackoverflow.com/a/59956926/4386148

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.