如何更新React中的嵌套状态,应该是不可变的吗?

J h*_*ung 6 javascript facebook reactjs

我们正在讨论如何在React中更新嵌套状态.国家应该是不可变的吗?优雅地更新状态的最佳做法是什么?

假设您的状态结构如下所示:

this.state = { 
                numberOfStudents: "3",
                gradeLevel: "5",
                students : [ 
                    { id : "1234",
                      firstName: "John",
                      lastName: "Doe",
                      email: "johndoe@mail.com"
                      phoneNumer: "12345"

                    },
                    { id : "56789",
                      firstName: "Jane",
                      lastName: "Doe",
                      email: "janedoe@mail.com"
                      phoneNumer: "56789"
                    },
                    { id : "11111",
                      firstName: "Joe",
                      lastName: "Doe",
                      email: "joedoe@mail.com"
                      phoneNumer: "11111"
                    }
                ]
            }
Run Code Online (Sandbox Code Playgroud)

然后我们想要更新joe doe的电话号码.我们有两种方法可以做到:

mutate state + force update to rerender

this.state.students[2].phoneNumber = "9999999";
this.forceUpdate();
Run Code Online (Sandbox Code Playgroud)

mutate state + setState with mutated state

this.state.students[2].phoneNumber = "9999999";
this.setState({
     students: this.state.students
});
Run Code Online (Sandbox Code Playgroud)

Object.assign,这仍然会改变状态,因为newStudents只是对同一个对象的一个​​新引用this.state指向

const newStudents = Object.assign({}, this.state.students);
newStudents[2].phoneNumber = "9999999"
this.setState({
     students: newStudents
});
Run Code Online (Sandbox Code Playgroud)

更新immutability helper(https://facebook.github.io/react/docs/update.html)+ setState.如果我们在每个学生对象中都有address.street,address.city,address.zip并想要更新街道,这会很快变得丑陋.

const newStudents = React.addons.update(this.state.students, {2: {phoneNumber: {$set:"9999999"}}});
this.setState({
     students: newStudents
})
Run Code Online (Sandbox Code Playgroud)

setState的react doc的最后一行指出:

永远不要直接改变this.state,因为之后调用setState()可能会替换你所做的突变.把this.state看作是不可变的. https://facebook.github.io/react/docs/react-component.html

文档声明我们不应该使用forceUpdate来重新渲染:

通常你应该尽量避免使用forceUpdate(),只能在render()中读取this.props和this.state.

为什么会这样,如果我们改变状态并在之后调用setState会发生什么?在什么情况下setState()会替换我们制作的突变?这是一个非常令人困惑的陈述.有人可以解释我们上面使用的每个场景可能的复杂性来设置状态.

Pin*_*eda 2

您声明:

“Object.assign,这仍然会改变状态,因为 newStudents 只是对 this.state 指向的同一对象的新引用”

这种说法是不正确的
Object.assign 改变传入其第一个参数的状态。由于您传递了一个空对象文字 ( {}) ,因此您正在改变新的对象文字不是this.state


一些背景:

不可变状态的原理与函数式编程有关。

它在 React 中非常有用,因为它为 React 提供了一种了解状态是否已更改的方法,它有用的一个用例是优化组件何时应重新渲染

考虑具有嵌套对象的复杂状态的情况。改变状态的值会改变状态内属性的值,但不会改变对象的引用。

this.state = {nestObject:{nestedObj:{nestObj:'yes'}}};

// mutate state
this.state.nestObject.nestedObj.nestObj= 'no';
Run Code Online (Sandbox Code Playgroud)

我们如何知道 React 是否应该重新渲染组件?

  1. 深度平等检查?想象一下在复杂的状态下会是什么样子,每个状态更新需要数百甚至数千次检查......
  2. 无需检查更改,只需强制 React 在每次状态更改时重新渲染所有内容...

后两种方法是否有替代方案?


不变的方式

通过创建一个新对象(因此也是一个新引用),复制旧状态并Object.assign对其进行变异,您可以操作状态值并更改对象引用。

使用不可变状态方法,我们只需检查对象引用是否相等即可知道状态是否已更改。

以下评论中的反对者的简化示例:

考虑这个简单的例子:

this this.state = { someValue: 'test'}
var newState = Object.assign({}, this.state);
console.log(newState);                  // logs: Object {someValue: "test"]  
console.log(this.state);                // logs: Object {someValue: "test"]

// logs suggest the object are equal (in property and property value at least...

console.log(this.state === this.state); // logs: true

console.log(this.state === newState);   // logs: false.  Objects are 
                                        // pass-by-reference, the values stored
                                        // stored in this.state AND newState
                                        // are references.  The strict equality
                                        // shows that these references
                                        // DON'T MATCH so we can see
                                        // that an intent to modify
                                        // state has been made
Run Code Online (Sandbox Code Playgroud)