artsnr
artsnr

Reputation: 1052

React Native UseEffect hook return function never gets called

I need to update date after 12 am. So, I am setting a recursive timer to calculate seconds till next day and update If user is on the same screen. I am noticing that the timer unsubscribe function never gets called (whether the app goes in background, app is killed or navigation state changes)

  const [selectedDate, setSelectedDate] = useState(new Date());
  let timer;

  useEffect(() => {
    console.log("RENDERING");
    updateDateAfter12am();
    return () => {
      console.log("CLEARING TIMER");
      clearTimeout(timer);
    };
  }, []);

  const updateDateAfter12am = () => {
    const currentDate = new Date();
    const secondsUntilEndOfDate = getSecondsUntilEndOfDate(currentDate);
    timer = setTimeout(() => {
      if (isSameDay(selectedDate, currentDate)) {
        setSelectedDate(new Date());
      }
      updateDateAfter12am();
    }, secondsUntilEndOfDate * 1000);
  };

  const isSameDay = (d1, d2) => {
    return (
      d1.getFullYear() === d2.getFullYear() &&
      d1.getMonth() === d2.getMonth() &&
      d1.getDate() === d2.getDate()
    );
  };

  const getSecondsUntilEndOfDate = (d) => {
    var h = d.getHours();
    var m = d.getMinutes();
    var s = d.getSeconds();
    return 24 * 60 * 60 - h * 60 * 60 - m * 60 - s;
  };

Upvotes: 0

Views: 869

Answers (1)

dglozano
dglozano

Reputation: 6607

I have created a very simple code sandbox to test your code, and it looks like it is behaving correctly: the useEffect cleanup function is being called whenever the component is unmounted, as stated in the docs.

GIF showcasing that it works fine

import React from "react";
import "./styles.css";

export default function App() {
  const [mounted, setMounted] = React.useState(true);
  return (
    <div className="App">
      <button onClick={() => setMounted((prev) => !prev)}>Mount/Unmount</button>
      {mounted && <Test />}
    </div>
  );
}

const Test = () => {
  const [selectedDate, setSelectedDate] = React.useState(new Date());
  let timer;

  React.useEffect(() => {
    console.log("RENDERING");
    updateDateAfter12am();
    return () => {
      console.log("CLEARING TIMER");
      clearTimeout(timer);
    };
  }, []);

  const updateDateAfter12am = () => {
    const currentDate = new Date();
    const secondsUntilEndOfDate = getSecondsUntilEndOfDate(currentDate);
    timer = setTimeout(() => {
      if (isSameDay(selectedDate, currentDate)) {
        setSelectedDate(new Date());
      }
      updateDateAfter12am();
    }, secondsUntilEndOfDate * 1000);
  };

  const isSameDay = (d1, d2) => {
    return (
      d1.getFullYear() === d2.getFullYear() &&
      d1.getMonth() === d2.getMonth() &&
      d1.getDate() === d2.getDate()
    );
  };

  const getSecondsUntilEndOfDate = (d) => {
    var h = d.getHours();
    var m = d.getMinutes();
    var s = d.getSeconds();
    return 24 * 60 * 60 - h * 60 * 60 - m * 60 - s;
  };

  return <p>{selectedDate.toString()}</p>;
};

Edit SO-66212721

So the problem might be that you are not actually unmounting your component. If you need help with that, you should post a Complete, Minimal, Reproducible example.

Upvotes: 1

Related Questions