Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use an effect once, but still use state, with React Hooks?

I am trying to set an interval on mount/unmount only, but allow the callback to have one variable from state.

I have code like this:

const [cachedData, setCachedData] = useState(false);

async function refreshData() {
  const data = await axios('http://www.example.com/');
  setCachedData(data);
}

useEffect(() => {
  let interval;
  async function fetch() {
    await refreshData();
    interval = setInterval(refreshData,5000);
    console.log('set interval to', interval)
  }

  fetch();

  return () => {
    console.log('clearing interval', interval);
    clearInterval(interval);
  }
}, []);

I am running into a catch-22. The second argument of useEffect says what variables to pay attention to. I've read making it an empty array makes this only mount/unmount rather than on any state updates. The issue I've found is that doing that, means refreshData has no access to cachedData, so I can't know I have valid data (to avoid the XHR request for an amount of time). If I pass cachedData into second argument of useEffect, it will have the variable but run more than it should. Not sure of a way around this.

I should note that if I pass cachedData to the second arg, and console.log by the clear and the setting of the interval, my console outputs something like this:

clearing interval undefined
set interval to 5
set interval to 7

So it seemingly runs the unmount and then the effect twice over without clearing again. This causes a double axios call.

like image 987
Dave Stein Avatar asked Dec 06 '25 04:12

Dave Stein


1 Answers

You can use a ref to get at the current cachedData with something like the following:

const [cachedData, setCachedData] = useState(false);
const cachedDataRef = useRef(cachedData);

useEffect(() => {
  // Update ref in effect so that render has no side effects.
  cachedDataRef.current = cachedData;
}, [cachedData]);

useEffect(() => {

  async function refreshData() {
    if (cachedDataRef.current) {
      // do something different
    } else {
      const data = await axios('http://www.example.com/');
      setCachedData(data);
    }
  }
  let interval;
  async function fetch() {
    await refreshData();
    interval = setInterval(refreshData,5000);
    console.log('set interval to', interval)
  }

  fetch();

  return () => {
    console.log('clearing interval', interval);
    clearInterval(interval);
  }
}, []);
like image 109
Ryan Cogswell Avatar answered Dec 09 '25 19:12

Ryan Cogswell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!