简体   繁体   中英

Reactjs - SetTimeout with state change re-render

I'm currently running into some issues whilst developing a Typescript React App. Underneath is my current code..

But it's not behaving like I would want it to behave. :)

So what I would like to achieve is that the data with getData(depth) runs whenever the component is being loaded and afterwards every 5 seconds.

But when the Depth changes with the Dropdown.item buttons, it should re-render and the getData() should be ran with the new depth value that we just set in the state.. and keep on rendering afterwards with the new value...

I've been struggling with this, so any help is very much appreciated!!

Thank you!

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

const chart = () => {
  const [depth, setDepth] = useState(20);
  const [chartData, setChartData] = useState({})

//Getting the data when the app initially renders and should keep rendering every 5 seconds after that.
//When the value of the depth changes, we should stop getting the data with the old depth //value and should start a new interval of 5 seconds and just keep running with the new //depth value
//When first entering the app, this should run immediately with the initial depth state //(20)

  useEffect(() => {
    const interval = setInterval(() => {
     //this code is not the actual code, just an example of what is running
      const data = getData(depth)
     //just fetched the new data, now setting it.. 
      setChartData(data)
    }, 5000);
    return () => clearInterval(interval);
  }, []);

  return (
<div>
  <div>
   <DropdownButton id="dropdown-basic-button" title="Depth Percentage">
    <Dropdown.Item onClick={() => setDepth(5)}>5%</Dropdown.Item>
    <Dropdown.Item onClick={() => setDepth(20)}>20%</Dropdown.Item>
   </DropdownButton>
  </div>
  <div>
   //Rendering the Chart here....
  </div>
</div>
  );
};

export default chart;

That's because useEffect hook take a second params called dependency array , where this dependency array is what matter for the inner callback(inisde useEffect) to access the latest values you want.

So your are not being totally truthful here, if the inner callback depends on depth to be in its latest update then you should include it in the dependency array

useEffect(() => { ... }, [ depth ]);

that's for the depth but writing this code will immediately cause problems because for each new depth value the inner callback will be called and the setInterval will re-run again (causing many many...many of intervals).

To solve this you should avoid using setInterval alll together in hooks based code.

If having interval is really important I have a suggestion for you


  const [intervalCount, setIntervalCount] = useState(0);
  const [depth, setDepth] = useState(20);
  const [chartData, setChartData] = useState({})


  useEffect(() => {
    // when depth change re-fetch data and set it
    const data: any = getData(depth);
    setChartData(data);
  }, [depth])


  // simulate set interval behavior 
  // each 5 s this function will be re-invoked
  useEffect(() => {

    // re-fetch data and set it
    const data: any = getData(depth);
    setChartData(data);

    // wait 5 s before cause a re-render
    setTimeout(() => {
      setIntervalCount(count => count + 1);
    }, 5000);

  }, [intervalCount]);



Updated: After rading from Dan Abramov blog

you can find a better elegant solution that use setInterval and hooks

Making setInterval Declarative with React Hooks

He made a custom hook called useInterval

function useInterval(callback, delay) {
  const savedCallback = useRef();

  // Remember the latest callback.
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  // Set up the interval.
  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

Usage be like

  useInterval(() => {
    // Your custom logic here
    setCount(count + 1);
  }, 1000);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM