繁体   English   中英

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

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

当您调整 window 的大小时,我有一个事件将显示桌面侧边栏或移动侧边栏。 如果 window 小于但是如果我在桌面 window 中,有些变量不会立即更新以显示侧边栏,我可以使用 class

我创建了一个沙箱https://codesandbox.io/s/sidebar-hooks-8oefi来查看代码,我在 App_class.js 中有 class 组件,如果我在 App 中替换它可以工作,但带有钩子 (App_hooks.js)。 js 文件,默认情况下在 App.js 中)我不能让它工作

感谢您的关注。 我期待着您的回复。

使用 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;

使用 setState 不起作用

问题是在这里设置isMobile值后,

setMobile(isMobile => newValueMobile);

您立即引用该值,

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

由于setStateasync性质,您总是在这里获得isMobile的先前值。

要完成这项工作,您需要对代码进行一些更改。

您正在直接改变previousWidth值,您应该在 state 中有previousWidth并使用设置器 function 来更改该值。

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

setPreviousWidth(width);  //setter function

您无法在setState之后立即获得价值。 您应该使用另一个useEffectisMobilepreviousWidth作为依赖数组。

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

演示

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

编辑:以下代码说明了您的问题的更简单版本:

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>
  )
}

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

@ravibagul91 答案有效,因为它从 useEffect 访问useEffect ,而不是从事件处理程序访问。

您可能想检查: React hooks behavior with event listenerWhy am I see stale props or state inside my function? ,它会让您更好地了解正在发生的事情。

暂无
暂无

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

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