【问题标题】:Mutating state from React's useState hook从 React 的 useState 钩子改变状态
【发布时间】:2019-08-06 06:59:54
【问题描述】:

是吗,为什么从 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 })
}

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

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

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

    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>

    您可以将一个函数作为setValues 的第一个参数,就像您在类组件setState 中习惯的那样,该函数反过来将获得正确的状态作为参数,返回的将是新的状态。

    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>

    【讨论】:

    • 是的,但它本质上是错误的吗?您编写的第一个示例不是 OP 所要求的。虽然我同意第二个示例是最佳实践,但它仍然不能回答问题。
    • @erez 是的,变异状态是错误的。改变一个对象不会改变对象引用。当 React 看到对象引用没有改变时,它不会触发更新。变异状态可能会出现奇怪的错误,并且很难找到。顺便说一句,OP 正在使用扩展运算符,因此 React 每次都会获得一个新对象,因此它与 NOT MUTATING 状态相同。
    【解决方案2】:

    基本上我会为了纯洁而避免以这种方式改变状态。

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

    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>

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

    【讨论】:

      猜你喜欢
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 2020-11-23
      • 1970-01-01
      • 2020-08-20
      相关资源
      最近更新 更多