Home > database >  React won't reload function despite state change
React won't reload function despite state change

Time:09-28

I have a state that tracks the window width:

const [innerWidth, setInnerWidth] = useState(window.innerWidth)

In useEffect, I create a resize eventListener which sets the state to the new width:

  useEffect(() => {
    document.addEventListener('resize', () => {
      setInnerWidth(window.innerWidth)
    })
  }, [])

Lastly, I have a function test that logs the innerWidth every 5 seconds, with an interval started in useEffect

  function test() {
    console.log(innerWidth)
  }

  useEffect(() => {
     setInterval(test, 5000)
  }, [])

Unfortunately, despite any resize that happen, the test() function keeps on logging the original innerWidth value.

How can I tell react to reload the test function as well?

EDIT:

The perpetual log of the innerWidth was just a simplification of my actual use case. Actually, the timer is shifting an element on the x-axis, and I need to know when it exceeds the width to stop the execution and start again.

Creating and invalidating a loop every time the window changes, like in several answers you've given, temporarily stops the shifting of my element, as the loop gets invalidated. I would like to avoid this.

CodePudding user response:

The useEffect created a closure around the original values, so that's all it ever logs. You'd need the effect to update any time the value changes, by adding it to the dependency array:

useEffect(() => {
  setInterval(test, 5000)
}, [innerWidth])

This would of course create a new interval on every state change. So the useEffect should return a function which cancels the interval:

useEffect(() => {
  const x = setInterval(test, 5000);
  return () => clearInterval(x);
}, [innerWidth])

That way there's only one interval running at any given time.

Though this begs the question... Why? If the goal is to log the value of innerWidth to observe its changes, then why re-log the same value every 5 seconds indefinitely? Skip the test function and the interval entirely and just log the value any time it changes:

useEffect(() => {
  console.log(innerWidth);
}, [innerWidth])

CodePudding user response:

Can you change the test function to an anonymous function?

const test = () => {
    console.log(innerWidth);
};

Change you useEffect:

useEffect(() => {
    window.addEventListener('resize', () => {
        setInnerWidth(window.innerWidth);
    });
}, [setInnerWidth]);

CodePudding user response:

This code works for me:

const [innerWidth, setInnerWidth] = useState(window.innerWidth);

useEffect(() => {
  const updateWidth = () => {
    setInnerWidth(window.innerWidth);
  };
  window.addEventListener("resize", updateWidth);
}, []);

useEffect(() => {
  console.log(innerWidth);
}, [innerWidth]);

I attached the eventlistener to the window element. In your case the timer should also show the updated state value.

Sandbox

  • Related