簡體   English   中英

useState 掛鈎設置器錯誤地覆蓋 state

[英]useState hook setter incorrectly overwrites state

這是問題所在:我試圖在單擊按鈕時調用 2 個函數。 這兩個函數都更新了 state(我正在使用 useState 掛鈎)。 第一個 function 將 value1 正確更新為“新 1”,但在 1 秒(setTimeout)之后,第二個 function 觸發,並將值 2 更改為“新 2”但是。 它將 value1 設置回 '1'? 為什么會這樣? 提前致謝!

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState({ ...state, value1: "new 1" });
  };
  const changeValue2 = () => {
    setState({ ...state, value2: "new 2" });
  };

  return (
    <>
      <button
        onClick={() => {
          changeValue1();
          setTimeout(changeValue2, 1000);
        }}
      >
        CHANGE BOTH
      </button>
      <h1>{state.value1}</h1>
      <h1>{state.value2}</h1>
    </>
  );
};

export default Test;

歡迎來到封閉地獄 發生此問題是因為每當調用setState時, state都會獲得新的 memory 引用,但函數changeValue1changeValue2 ,由於關閉,保留舊的初始state引用。

確保來自changeValue1changeValue2setState獲取最新 state的解決方案是使用回調(將之前的 state 作為參數):

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
  };
  const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
  };

  // ...
};

您可以在此處此處找到有關此關閉問題的更廣泛討論。

你的函數應該是這樣的:

const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
};
const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
};

因此,當觸發操作時,您可以通過使用先前的 state 確保您沒有丟失當前 state 中的任何現有屬性。 因此,您也避免了必須管理閉包。

changeValue2被調用時,初始 state 被保留,因此 state 回到初始 state 然后寫入value2屬性。

下次調用changeValue2時,它會保存 state {value1: "1", value2: "new 2"} ,因此value1屬性被覆蓋。

您需要一個箭頭 function 作為setState參數。

 const Test = () => { const [state, setState] = React.useState({ value1: "1", value2: "2" }); const changeValue1 = () => { setState(prev => ({...prev, value1: "new 1" })); }; const changeValue2 = () => { setState(prev => ({...prev, value2: "new 2" })); }; return ( <React.Fragment> <button onClick={() => { changeValue1(); setTimeout(changeValue2, 1000); }} > CHANGE BOTH </button> <h1>{state.value1}</h1> <h1>{state.value2}</h1> </React.Fragment> ); }; ReactDOM.render(<Test />, document.getElementById('root'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>

發生的事情是changeValue1changeValue2從它們創建的渲染中看到 state ,因此當您的組件第一次渲染時,這兩個函數會看到:

state= {
  value1: "1",
  value2: "2"
}

單擊按鈕時,首先調用changeValue1並將 state 按預期更改為{value1: "new1", value2: "2"}

Now, after 1 second, changeValue2 is called, but this function still see the initial state ( {value1; "1", value2: "2"} ), so when this function updates the state this way:

setState({...state, value2: "new 2" });

你最終會看到: {value1; "1", value2: "new2"} {value1; "1", value2: "new2"}

資源

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM