Search code examples
javascriptreactjsapiobserver-pattern

ResizeObserver API doesn't get the updated state in React


I am using ResizeObserver to call a function when the screen is resized, but I need to get the updated value of a state within the observer in order to determine some conditions before the function gets invoked.

It's something like this:

let [test, setTest] = React.useState(true)

const callFunction = () => {
    console.log('function invoked')
    setTest(false) // => set 'test' to 'false', so 'callFunction' can't be invoked again by the observer
}

const observer = React.useRef(
    new ResizeObserver(entries => {
        console.log(test) // => It always has the initial value (true), so the function is always invoked
        if (test === true) {
            callFunction()
        }
    })
)

React.useEffect(() => {
    const body = document.getElementsByTagName('BODY')[0]
    observer.current.observe(body)

    return () =>  observer.unobserve(body)      
}, [])

Don't worry about the details or why I'm doing this, since my application is way more complex than this example.

I only need to know if is there a way to get the updated value within the observer. I've already spent a considerable time trying to figure this out, but I couldn't yet.

Any thoughts?


Solution

  • The problem is, you are defining new observer in each re render of the component, Move it inside useEffect will solve the problem. also you must change this observer.unobserve(body) to this observer..current.unobserve(body).

    I have created this codesandbox to show you how to do it properly. this way you don't need external variable and you can use states safely.

    import { useEffect, useState, useRef } from "react";
    
    const MyComponent = () => {
      const [state, setState] = useState(false);
    
      const observer = useRef(null);
    
      useEffect(() => {
        observer.current = new ResizeObserver((entries) => {
          console.log(state);
        });
    
        const body = document.getElementsByTagName("BODY")[0];
        observer.current.observe(body);
    
        return () => observer.current.unobserve(body);
      }, []);
    
      return (
        <div>
          <button onClick={() => setState(true)}>Click Me</button>
          <div>{state.toString()}</div>
        </div>
      );
    };
    
    export default MyComponent;