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

时间:2019-10-01 22:40:48

标签: 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;

4 个答案:

答案 0 :(得分:25)

欢迎来到封闭地狱。发生此问题的原因是,每当调用setState时,state都会获得新的内存引用,但是由于关闭,函数changeValue1changeValue2会保留旧的初始{{1 }}参考。

一种用于确保statesetState中的changeValue1获得最新状态的解决方案是使用callback(具有最新的状态作为参数):

changeValue2

您可以找到有关herehere这个关闭问题的更广泛的讨论。

答案 1 :(得分:18)

您的功能应如下所示:

.*

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

答案 2 :(得分:5)

调用changeValue2时,将保持初始状态,以便该状态返回初始状态,然后写入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'));
<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>

答案 3 :(得分:3)

发生的事情是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"}

source