简体   繁体   中英

React hooks - setState is not updating the state properties to show sidebar

I have a event when you resize the window will show desktop sidebar or mobile sidebar. if window is less than But there are variables that aren't updated immediately to show sidebar if I'm in desktop window, I could that with class

I've created a sandbox https://codesandbox.io/s/sidebar-hooks-8oefi to see the code, I have the class component in App_class.js which if I replace in App it works, but with hooks (App_hooks.js file, by default in App.js) I can't make it works

Thanks for your attention. I'm looking forward to your reply.

with class I could do that using:

if (isMobile !== wasMobile) {
   this.setState({
      isOpen: !isMobile
   });
}
 const App = props => {
  //minicomponent to update width
  const useListenResize = () => {
    const [isOpen, setOpen] = useState(false);
    const [isMobile, setMobile] = useState(true);
    //const [previousWidth, setPreviousWidth] = useState( -1 );

    let previousWidth = -1;

    const updateWidth = () => {
      const width = window.innerWidth;
      const widthLimit = 576;
      let newValueMobile = width <= widthLimit;
      setMobile(isMobile => newValueMobile);
      const wasMobile = previousWidth <= widthLimit;

      if (isMobile !== wasMobile) {
        setOpen(isOpen => !isMobile);
      }
      //setPreviousWidth( width );
      previousWidth = width;
    };

    useEffect(() => {
      updateWidth();
      window.addEventListener("resize", updateWidth);
      return () => {
        window.removeEventListener("resize", updateWidth);
      };
      // eslint-disable-next-line
    }, []);

    return isOpen;
  };

  const [isOpen, setOpen] = useState(useListenResize());

  const toggle = () => {
    setOpen(!isOpen);
  };

  return (
    <div className="App wrapper">
      <SideBar toggle={toggle} isOpen={isOpen} />
      <Container fluid className={classNames("content", { "is-open": isOpen })}>
        <Dashboard toggle={toggle} isOpen={isOpen} />
      </Container>
    </div>
  );
};

export default App;

with setState didn't work

The issue is after setting isMobile value here,

setMobile(isMobile => newValueMobile);

You are immediately refering that value,

if (isMobile !== wasMobile) {
   setOpen(isOpen => !isMobile);
}

Due to async nature of setState , your are getting previous value of isMobile here all the times.

To make this work you need to make some change to your code.

You are directly mutating previousWidth value, you should have previousWidth in a state and use setter function to change the value.

const [previousWidth, setPreviousWidth] = useState(-1);

setPreviousWidth(width);  //setter function

You cannot get value immediately after setState . You should use another useEffect with isMobile and previousWidth as dependency array.

useEffect(() => {
   const wasMobile = previousWidth <= widthLimit;
   if (isMobile !== wasMobile) {
     setOpen(isOpen => !isMobile);
   }
}, [isMobile, previousWidth]);

Demo

Since updateWidth is registered once on component mount with useEffect, then it will refer to a stale state ( isMobile ), which is always true, therefore, setOpen(isOpen => !isMobile) will never work.

EDIT: The following code illustrates a simpler version of your issue:

const Counter = () => {
  const [count, setCount] = useState(0)

  const logCount = () => {
    console.log(count);
  }

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  )
}

If you run this code, you will notice that even if you change the value of count by clicking on the button, you will always get the initial value when resizing the browser (check the console), and that's because logCount refer to a state that was obtained at the moment it was defined.

@ravibagul91 answer works, because it access the state from useEffect , and not from the event handler.

You may wanna check: React hooks behaviour with event listener and Why am I seeing stale props or state inside my function? , it will give you a better idea of what's going on.

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