【问题标题】:Does React create a deep or shallow copy of prevState internally?React 是在内部创建 prevState 的深拷贝还是浅拷贝?
【发布时间】:2020-07-23 06:01:39
【问题描述】:

我刚开始编程,最近用 React 编写了我的第一个应用程序,虽然它确实有效,但我不确定我是否正确处理了我的内部状态。我的问题是 setState 方法是否创建了“prevState”的不可变深层副本?举个例子:

 menuAddRecHandler = () => {
    this.setState((prevState) => {
      const updatedState = {...prevState};
      const dayInd = updatedState.menu.recipes.findIndex(item => item.dayNum === updatedState.menuInternal.currentDay);
      updatedState.menu.recipes[dayInd].recList.push({
        name: prevState.menuInternal.menuRecSelect,
        portions: prevState.menuInternal.menuNumPortInput
      });
      return updatedState;
    })
  }

在我的处理程序中,我将一个对象推送到 updatedState 对象的嵌套数组,该数组是由扩展运算符从 prevState 复制过来的。现在我知道扩展操作符只做一个浅拷贝,但是 setState 方法内部提供的 prevState 也是浅的,这是否意味着我实际上是通过调用这个处理程序直接改变我的状态?如果是这样,我该如何解决这个问题?谢谢。

【问题讨论】:

    标签: reactjs state immutability setstate


    【解决方案1】:

    来自docs

    第一个参数是带有签名的更新函数:

    (state, props) => stateChange

    state 是对应用更改时的组件状态的引用。它不应该直接突变。相反,应该通过基于 state 和 props 的输入构建一个新对象来表示更改。

    如何在不变异的情况下达到相同的结果?

    menuAddRecHandler = () => {
      this.setState((prevState) => {
        return {
          ...prevState,
          menu: {
            ...prevState.menu,
            recipes: prevState.menu.recipes.map((item) => {
              if(item.dayNum === prevState.menuInternal.currentDay) {
                return {
                  ...item,
                  recList: [
                    ...item.recList,
                    {
                      name: prevState.menuInternal.menuRecSelect,
                      portions: prevState.menuInternal.menuNumPortInput
                    }
                  ]
                }
              }
              return item;
            })
          }
        }
      });
    }
    

    这显然很容易出错,其想法是减少更深的状态。 Redux 的文档offer some tips for normalizing state

    您还可以查看不可变状态库,例如 ImmutableJSImmer

    【讨论】:

    • 更好的答案。
    • 非常好的答案,这些链接肯定会对作者做出更可维护的状态有用。
    • 谢谢。我想我现在将专注于学习 Redux 来清理我造成的烂摊子。
    • @Glogov 不,你不必学习 Redux,该链接仅用于规范化状态,它也可以应用于本地状态。有一个page in redux docs可以帮助你评估是否需要redux
    • @Agney 我会调查一下,现在我正在阅读您提到的 Immer 库。用这个特定的应用程序解决我的问题似乎是一种不错的、不复杂的方法。谢谢你提出来。
    【解决方案2】:

    According to the docs根本不是复制品

    state 是对应用更改时的组件状态的引用。它不应该直接变异。

    制作深拷贝通常对性能不利,但如果必须这样做,您可以这样做:

    const updatedState = JSON.parse(JSON.stringify(prevState));
    

    【讨论】:

    • 我明白了。您的方法应该可以解决问题,因为它是一个相对较小的应用程序。但是,我知道我一开始就不应该创建这样一个嵌套的内部状态?
    • 是的,有一个非常深的状态使得它很难使用并且修改成本很高(您至少需要重新创建作为您正在修改的值的父对象的每个对象)
    【解决方案3】:

    您的示例中有很多“噪音”,所以我会保持简单:

    state = {  name: `Dennis`, age: 89 }
    
    this.setState((prevState) => {
      return { age: prevState.age + 1 }; 
    }); // { name: `Dennis`, age: 90 }
    
    this.setState({ name: `John` }); // { name: `John`, age: 89 }
    

    在类组件中,this.setState浅合并与之前的状态,prevState是实际的状态引用,即:{ name: "Dennis", age: 89 }

    updater 函数接收到的 state 和 props 都保证是最新的。更新器的输出与状态浅合并。

    注意不是the case with functional components(常见错误)。

    与类组件中的 setState 方法不同,useState 不会自动合并更新对象。

    【讨论】:

      猜你喜欢
      • 2011-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-30
      • 1970-01-01
      • 1970-01-01
      • 2010-11-19
      相关资源
      最近更新 更多