1. setState概述
setState
是React框架中,用于更新組件狀態的方法。
setState
方法由React組件繼承自 React.Component
類的一部分。通過調用 setState,可以告訴 React要更新組件的狀態,并觸發組件的重新渲染。
this.setState(newState, callback);
newState
是一個對象,它包含了需要更新的狀態屬性和值。它將合并到組件的當前狀態中。
callback
是一個回調函數,它在 setState 更新狀態完成之后被調用。
2. setState為何使用不可變值
- 什么是 不可變值?
React框架中的“不可變值”的概念,通常指的是在編寫React組件時,盡量避免直接修改數據,而是創建新的數據副本。
- setState為何使用不可變值?
在React中,組件的狀態(state)應該保持不變。通過使用 setState
方法更新狀態時,React會合并現有狀態和新狀態,而不是直接修改狀態。 這確保 React 可以正確地追蹤狀態的變化并進行必要的更新。
同時,直接修改數據可能會引發意外的副作用,尤其是在多個組件之間共享數據時。通過使用不可變值,可以減少出現這種問題的可能性。
常用的處理不可變值的工具包括 Object.assign
、數組的 concat
、slice
等方法。對數組或者對象使用slice()
方法,可以生成該數組或對象的副本,類似于深拷貝。
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要在構造函數中定義
constructor(props){this.state = {count: 0}
}
因為 setState 是異步的,因此在 setState 方法執行完后打印狀態,是拿不到更新后的值,只能拿到當前狀態的值。
this.setState({count: this.state.count + 1;
})
console.log(this.state.count) //打印結果為 0
如果想要拿到最新的狀態,需要在setState中,寫一個回調函數。此時count的結果為1
this.setState({count: this.state.count + 1},() => {console.log(this.state.count)}
)
setTimeout
中的setState
是同步的。此時打印出來的count結果就是1
setTimeout(()=>{this.setState({count: this.state.count + 1})console.log(this.state.count) // 打印結果為1
}, 0)
- 在自定義的DOM事件中,setState也是同步的。
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次。
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。
上述代碼的思想類似于
Object.assign({count: 1}, {count: 1}, {count: 1}) // 執行結果為 {count: 1}
- 為了防止這種情況發生,可以在setState中傳入更新函數(updater function),它接受先前的狀態并返回更新后的狀態。這能夠確保使用的是最新的狀態。
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)。