useState hook setter 错误地覆盖状态

IT技术 javascript reactjs react-hooks
2021-05-09 05:20:25

问题是:我试图在单击按钮时调用 2 个函数。这两个函数都会更新状态(我正在使用 useState 钩子)。第一个函数将 value1 正确更新为“new 1”,但在 1s (setTimeout) 后第二个函数触发,并将值 2 更改为“new 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;
4个回答

欢迎来到封闭地狱发生此问题setState因为无论何时被调用,state都会获得一个新的内存引用,但由于关闭,函数changeValue1andchangeValue2保留了旧的初始state引用。

确保setStatefromchangeValue1changeValue2获取最新状态的解决方案是使用回调(将先前状态作为参数):

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

因此,您可以通过在触发操作时使用之前的状态来确保当前状态中没有丢失任何现有属性。也因此您避免必须管理关闭。

changeValue2被调用时,初始状态保持如此状态转变回inital状态,然后value2财产被写入。

之后的下一次changeValue2调用时,它持有 state {value1: "1", value2: "new 2"},因此value1属性被覆盖。

您需要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= {
  value1: "1",
  value2: "2"
}

当您单击按钮时,changeValue1首先调用它并将状态更改{value1: "new1", value2: "2"}为预期。

现在,在 1 秒后,changeValue2被调用,但此函数仍看到初始状态 ( {value1; "1", value2: "2"}),因此当此函数以这种方式更新状态时:

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

你最终会看到:{value1; "1", value2: "new2"}.

来源