1. setState概述
setState
是React框架中,用于更新组件状态的方法。
setState
方法由React组件继承自 React.Component
类的一部分。通过调用 setState,可以告诉 React要更新组件的状态,并触发组件的重新渲染。
javascript
this.setState(newState, callback);
newState
是一个对象,它包含了需要更新的状态属性和值。它将合并到组件的当前状态中。
callback
是一个回调函数,它在 setState 更新状态完成之后被调用。
2. setState为何使用不可变值
- 什么是 不可变值?
React框架中的"不可变值"的概念,通常指的是在编写React组件时,尽量避免直接修改数据,而是创建新的数据副本。
- setState为何使用不可变值?
在React中,组件的状态(state)应该保持不变。通过使用 setState
方法更新状态时,React会合并现有状态和新状态,而不是直接修改状态。 这确保 React 可以正确地追踪状态的变化并进行必要的更新。
同时,直接修改数据可能会引发意外的副作用,尤其是在多个组件之间共享数据时。通过使用不可变值,可以减少出现这种问题的可能性。
常用的处理不可变值的工具包括 Object.assign
、数组的 concat
、slice
等方法。对数组或者对象使用slice()
方法,可以生成该数组或对象的副本,类似于深拷贝。
javascript
const list5Copy = this.state.list5.slice()
list5Copy.splice(2, 0, 'a') // 中间插入/删除
this.setState({
list1: this.state.list1.concat(100), // 追加
list2: [...this.state.list2, 100], // 追加
list3: this.state.list3.slice(0, 3), // 截取
list4: this.state.list4.filter(item => item > 100), // 筛选
list5: list5Copy // 其他操作
})
3. setState可能是异步的
setState是一个异步方法。这意味着React可能会对多个 setState 调用进行批处理,然后一次性更新组件的状态,而不是每次调用都立即更新。
然而,setTimeout
函数中的 setState 是同步的。在自定义的DOM事件中,setState也是同步的。
state要在构造函数中定义
javascript
constructor(props){
this.state = {
count: 0
}
}
因为 setState 是异步的,因此在 setState 方法执行完后打印状态,是拿不到更新后的值,只能拿到当前状态的值。
javascript
this.setState({
count: this.state.count + 1;
})
console.log(this.state.count) //打印结果为 0
如果想要拿到最新的状态,需要在setState中,写一个回调函数。此时count的结果为1
javascript
this.setState(
{count: this.state.count + 1},() => {console.log(this.state.count)}
)
setTimeout
中的setState
是同步的。此时打印出来的count结果就是1
javascript
setTimeout(()=>{
this.setState({
count: this.state.count + 1
})
console.log(this.state.count) // 打印结果为1
}, 0)
- 在自定义的DOM事件中,setState也是同步的。
javascript
bodyClickHandler = () => {
this.setState({
count: this.state.count + 1
})
console.log(this.state.count) // 打印结果为1
}
componentDidMount(){
document.body.addEventListener('click', this.bodyClickHandler)
}
4. setState何时合并state
- setState是异步更新的话,传入的参数是一个对象,那么多次更新同一个状态只会执行1次。
javascript
constructor(props){
this.state = {
count: 0
}
}
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
console.log(this.state.count) // 打印结果为 1
由于 setState 是异步的,React 会将这些更新一起批处理,然后应用它们。这意味着所有三个 setState 调用可能几乎同时执行,使用初始值 this.state.count,导致增加只有 1 而不是 3。
上述代码的思想类似于
javascript
Object.assign({count: 1}, {count: 1}, {count: 1}) // 执行结果为 {count: 1}
- 为了防止这种情况发生,可以在setState中传入更新函数(updater function),它接受先前的状态并返回更新后的状态。这能够确保使用的是最新的状态。
javascript
constructor(props){
this.state = {
count: 0
}
}
this.setState((prevState) => ({
count: prevState.count + 1
}));
this.setState((prevState) => ({
count: prevState.count + 1
}));
this.setState((prevState) => ({
count: prevState.count + 1
}));
console.log(this.state.count) // 打印结果为 3
5. React18中的setState
- 当React的版本小于等于17时,在组件中直接使用setState更新状态,是批处理的(即异步更新且合并相同状态)。在setTimeout方法和自定义DOM事件中,使用setState方法更新状态是同步的,不是批处理的。
- 然而,在React18版本中,无论是在组件中直接更新状态还是在自定义DOM事件和setTiemout方法中更新,都是自动批处理(Automatic Batching)。