Search code examples
javascriptreactjsasynchronousfetch

setInterval behaving sporadically in React app


I am trying to cast some data to state on an interval, I am trying to make a graph that updates every x seconds.

fuction func() {
    console.log('Hello, world!')
}

setInterval(func, 5000)

Instead of responding every 5 seconds as expected I get two responses every 10 seconds, this problem gets far worse when i try and cast to state as I actually want to.

Here is the response from my API call, I all I am trying to do is cast it to state and log the result.


{
  "_id": "60d5e8a81a68bb7b14d9f3b0",
  "symbol": "LTC",
  "perc": "3.55",
  "createdAt": "2021-06-25T14:31:04.677Z",
  "updatedAt": "2021-06-25T14:31:04.677Z",
  "__v": 0
}

When I use the below code, instead of a double result in double the time, it goes absolutes haywire.


let [ltcArb, setLtcArb] = useState([]);

    const fetchLtcArbHandler = () => {
        fetch("/arb/ltc")
            .then((res) => res.json())
            .then((json) => setLtcArb((prev) => [{ perc: json.perc, time: json.createdAt },...prev,]))
            .then(console.log(ltcArb));
    };

    function poes() {
        console.log("poes");
    }

    setInterval(fetchLtcArbHandler, 5000);

I have been knocking my head at this for hours and quite honestly lost some sleep last night. Any and all help will be appreciated, take it easy on me please I am still very very new here. Thanks


Solution

  • That's because you're using a functional component which is actually the render function of the component.

    It's being executed every time you re-render the component, thus every time you change the state.

    What you need is encapsulate it in a useEffect with an empty dependency array like this:

    useEffect(() => setInterval(fetchLtcArbHandler, 5000), []);
    

    Also, you need to return a cleanup function, otherwise the requests will run indefinitely even when the component is unmounted, while also causing a memory leak because of the potential closure context references:

    useEffect(() => {
      const interval = setInterval(fetchLtcArbHandler, 5000);
      return () => clearInterval(interval);
    }, []);