useState挂钩设置器错误地覆盖了状态

Bar*_*tek 30 javascript reactjs react-hooks

问题出在这里:我试图在单击按钮时调用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;
Run Code Online (Sandbox Code Playgroud)

Alb*_*res 25

欢迎来到封闭地狱。发生此问题的原因setState是,无论何时调用,state都会获取新的内存引用,但是由于闭包,函数changeValue1和会changeValue2保留旧的初始state引用。

确保setState从源changeValue1changeValue2获取最新状态的解决方案是使用回调(将新鲜的当前状态作为参数):

import React, { useState } from "react";

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

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

  // ...
};
Run Code Online (Sandbox Code Playgroud)

您可以在此处此处找到有关此关闭问题的更广泛讨论。


Dez*_*Dez 18

您的功能应如下所示:

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

因此,通过在触发操作时使用以前的状态,可以确保您不会丢失当前状态下的任何现有属性。这样也避免了必须管理闭包。


zma*_*mag 5

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

changeValue2此后下次调用时,它将保持状态{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'));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)