簡體   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