React官方文档中提到:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains. setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate().
If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
下面的代码分别是官方推荐和不推荐的两种改变state的方式:
class Dog extends Component {
constructor(props) {
super(props)
this.state = {
color: 'white',
age: 7,
son: {
color: 'gray',
age: 1
}
}
}
brushHair() {
//right
setState({color: 'white'})
//wrong
this.state.color = 'black';
}
}
但你会发现在实践中即便使用第二种写法也不会报任何错误,甚至还能得到预期结果,那么为什么这种方式会被诟病呢?下面我谈谈自己的理解:
React中state发生变化,组件就会更新。setState()被设计为异步的,其目的在于在某些情况下(如一次onClick中)延缓并将多次更新合并为一次从而优化组件性能,如果像第二种写法那样直接改变state的值,这种优化也就不存在了;
执行setState会按照React设计的执行顺序,调用其内部的各种函数,如果直接改变state,这些本应被调用的函数没有执行,可能会导致奇奇怪怪的错误;
因为setState()是异步的,所以上面的代码最终color的值可能是white,显然不符合正常的思维逻辑;
有时候我们希望在setState之后立即使用state的值,有两种方法可以实现这个需求:
方法一:
setState({ color: 'red' }, () => console.log(this.state.color));
即向setState传入一个回调函数,这个函数会在state发生变化之后被调用。
方法二:
setState({ age: 18 });
setState((prevState, props) => ({
age: ++prevState.age
}))
setState的第一个参数还可以是一个函数,这个函数的返回值就是要merge的state,区别在于这个setState会在之前的setState执行完毕后再执行,所以prevState是最新的state。
另外,当更新state的部分属性时,其他属性是不会受影响的,本质上是Object.assign({}, state, partialState),但仅限于第一层结构,如果像下面这样
brushSonHair() {
setState({
son: {
color: 'black'
}
})
}
上面的方法中setState改变的时第二层的属性值(son中的color),第一层的属性值(color age)不会受到影响,但son中的age属性就会丢失(可以理解为改变了son)。