简体   繁体   English

React hooks - setState 没有更新 state 属性以显示侧边栏

[英]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.当您调整 window 的大小时,我有一个事件将显示桌面侧边栏或移动侧边栏。 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如果 window 小于但是如果我在桌面 window 中,有些变量不会立即更新以显示侧边栏,我可以使用 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我创建了一个沙箱https://codesandbox.io/s/sidebar-hooks-8oefi来查看代码,我在 App_class.js 中有 class 组件,如果我在 App 中替换它可以工作,但带有钩子 (App_hooks.js)。 js 文件,默认情况下在 App.js 中)我不能让它工作

Thanks for your attention.感谢您的关注。 I'm looking forward to your reply.我期待着您的回复。

with class I could do that using:使用 class 我可以这样做:

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使用 setState 不起作用

The issue is after setting isMobile value here,问题是在这里设置isMobile值后,

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.由于setStateasync性质,您总是在这里获得isMobile的先前值。

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.您正在直接改变previousWidth值,您应该在 state 中有previousWidth并使用设置器 function 来更改该值。

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

setPreviousWidth(width);  //setter function

You cannot get value immediately after setState .您无法在setState之后立即获得价值。 You should use another useEffect with isMobile and previousWidth as dependency array.您应该使用另一个useEffectisMobilepreviousWidth作为依赖数组。

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.由于updateWidth在组件挂载时使用 useEffect 注册一次,因此它将引用过时的 state ( isMobile ),这始终是正确的,因此setOpen(isOpen => !isMobile)将永远无法工作。

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.如果你运行这段代码,你会注意到,即使你通过点击按钮改变了count的值,你在调整浏览器大小时总是会得到初始值(检查控制台),这是因为logCount引用了一个 state是在定义的那一刻获得的。

@ravibagul91 answer works, because it access the state from useEffect , and not from the event handler. @ravibagul91 答案有效,因为它从 useEffect 访问useEffect ,而不是从事件处理程序访问。

You may wanna check: React hooks behaviour with event listener and Why am I seeing stale props or state inside my function?您可能想检查: React hooks behavior with event listenerWhy am I see stale props or state inside my function? , it will give you a better idea of what's going on. ,它会让您更好地了解正在发生的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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