从 React 的 useState 钩子改变状态

Mutating state from React's useState hook

是吗,为什么从 React 的新 useState 钩子改变状态是个坏主意?我没有找到关于该主题的信息。

考虑以下代码:

const [values, setValues] = useState({})

// doSomething can be called once, or multiple times per render

const doSomething = (name, value) => {
  values[name] = value
  setValues({ ...values })
}

注意值的突变。由于每次渲染可以多次调用 doSomething,由于 setState:

的异步属性,这样做是行不通的
const doSomething = (name, value) => {
  setValues({ ...values, [name]: value })
}

在这种情况下,直接改变值的方法是否正确?

您永远不应该直接改变状态,因为如果您使用相同的对象引用更新状态,它甚至可能不会导致重新渲染。

const { useState } = React;

function App() {
  const [values, setValues] = useState({});

  const doSomething = (name, value) => {
    values[name] = value;
    setValues(values);
  };

  return (
    <div onClick={() => doSomething(Math.random(), Math.random())}>
      {JSON.stringify(values)}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

您可以像在 class 组件 setState 中一样,将函数作为第一个参数传递给 setValues,然后该函数将获得正确的状态参数,返回的将是新状态。

const doSomething = (name, value) => {
  setValues(values => ({ ...values, [name]: value }))
}

const { useState } = React;

function App() {
  const [values, setValues] = useState({});

  const doSomething = (name, value) => {
    setValues(values => ({ ...values, [name]: value }));
  };

  return (
    <div onClick={() => doSomething(Math.random(), Math.random())}>
      {JSON.stringify(values)}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

基本上,为了纯粹起见,我会避免以这种方式改变状态。

但是,我认为在这种情况下它完全没问题。当你在状态的内部级别改变一个值时,React 不会注意到它。仅当使用 新引用 调用 setValues() 时,React 才注意到新的渲染正在等待处理。

const { useState } = React;

function App() {
  const [values, setValues] = useState({ num: 0 });

  const handleClick = () => {
    doSomething();
    doSomething();
  }
  
  const doSomething = () =>
    setValues((values) => {
      values.num += 1;
      return { ...values };
    });

return (
  <div onClick={handleClick}>
    {JSON.stringify(values)}
  </div>
);
}

ReactDOM.render( < App / > , document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

(如果有人能提供一个反例我很乐意看到)