例子快照
class App extends Component{
state = {
name:'rose',
age: 0,
};
click = () => {
this.setState((state)=>({name:'name1',age: state.age+1}))
this.setState((state)=>({name:'name2',age: state.age+1}))
this.setState((state)=>({name:'name3',age: state.age+1}))
this.setState({name:'name4', age: this.state.age+1})
this.setState((state)=>({name:'name5',age: state.age+1}))
};
render() {
console.log('render...');
const { name, age } = this.state;
return (
<div>
<span>{name + '---' + age}</span>
<button id='btn' onClick={this.click}>click me!</button>
</div>
)
}
}
提問:
- render打印了幾次?
- name 和 age最終是多少?
setState之后立馬渲染?
觸發(fā)了setState之后,會立馬觸發(fā)render!
誒!不對!
來自官方的一段話:
setState() 將對組件 state 的更改排入隊列,并通知 React 需要使用更新后的 state 重新渲染此組件及其子組件。這是用于更新用戶界面以響應事件處理器和處理服務器數(shù)據(jù)的主要方式
將 setState() 視為請求而不是立即更新組件的命令。為了更好的感知性能,React 會延遲調用它,然后通過一次傳遞更新多個組件。React 并不會保證 state 的變更會立即生效。
setState() 并不總是立即更新組件。它會批量推遲更新。這使得在調用 setState() 后立即讀取 this.state 成為了隱患。為了消除隱患,請使用 componentDidUpdate 或者 setState 的回調函數(shù)(setState(updater, callback)),這兩種方式都可以保證在應用更新后觸發(fā)。如需基于之前的 state 來設置當前的 state,請閱讀下述關于參數(shù) updater 的內容。
除非 shouldComponentUpdate() 返回 false,否則 setState() 將始終執(zhí)行重新渲染操作。如果可變對象被使用,且無法在 shouldComponentUpdate() 中實現(xiàn)條件渲染,那么僅在新舊狀態(tài)不一時調用 setState()可以避免不必要的重新渲染
大概就是,setState不是立馬更新的,而是把更改之后的state排入一個隊列,在要渲染的時候,批量處理,也就是拿隊列里面最后進來的那個state...
take is cheap...
例子??:
click=()=>{
console.log('start!')
this.setState((state)=>({name:'name1',age: state.age+1}))
this.setState((state)=>({name:'name2',age: state.age+1}))
this.setState((state)=>({name:'name3',age: state.age+1}))
console.log('end!')
};
render(){
console.log('render!', this.state)
}
最后打?。?/p>
start!
end!
render! {name: "name3", age: 3}
雖然觸發(fā)setState了三次,但是render只執(zhí)行了一次,結果為最后setState的為準!也就是上面說的【修改之后的state】隊列的最新的一條數(shù)據(jù)!
總結
不是!
setState不是立馬更新的,而是把更改之后的state排入一個隊列,在要渲染的時候,批量處理,也就是拿隊列里面最后進來的那個state
多次setState,函數(shù)方式不會合并,對象方式會合并?
我一直覺得合并這個詞用來形容setState的行為不太好, 合并給人的感覺是,我寫了三行setState的代碼,最終只執(zhí)行了最后一行setState的代碼!
結果是最終只執(zhí)行了最后一行setState的代碼?
take is cheap...
例子??【對象方式】
click=()=>{
console.log('start!')
this.setState({name:'name1',age: this.state.age+1})
this.setState({name:'name2',age: this.state.age+1})
this.setState({name:'name3',age: this.state.age+1})
console.log('end!')
};
render(){
console.log('render!', this.state)
}
最后打印:
start!
end!
render! {name: "name3", age: 1}
最后打印的結果,是最后一個this.setState({name:'name3',age: this.state.age+1})的結果!因為age最終就是從0到了1,前面的兩個似乎絲毫沒有起作用!
在看例子??:
newState = (name) => {
console.log('pre:', this.state);
return {name,age: this.state.age+1};
}
click=()=>{
console.log('start!')
this.setState(this.newState('name1'))
this.setState(this.newState('name2'))
this.setState(this.newState('name3'))
console.log('end!')
};
最終打印:
start!
pre: {name: "rose", age: 0}
pre: {name: "rose", age: 0}
pre: {name: "rose", age: 0}
end!
render! {name: "name3", age: 1}
每次setState都執(zhí)行了!
至于為什么造成:
最后打印的結果,是最后一個
this.setState({name:'name3',age: this.state.age+1})的結果!因為age最終就是從0到了1
的錯覺,是因為 this.state.age 一直都是0。因為我們前面的setState沒有立馬更新state,所以age的結果也沒有改變。
總結
每次setState都會執(zhí)行,執(zhí)行之后的值沒有立馬更新this.state,所以每次在setState里面取到的this.state的值都是最初的值,造成了合并執(zhí)行最后一條setState的錯覺。
例子??【函數(shù)方式】
click=()=>{
console.log('start!')
this.setState((preState)=>({name:'name1',age: preState.age+1}))
this.setState((preState)=>({name:'name2',age: preState.age+1}))
this.setState((preState)=>({name:'name3',age: preState.age+1}))
console.log('end!')
};
render(){
console.log('render!', this.state)
}
最后打印:
start!
end!
render! {name: "name3", age: 3}
最后打印的結果age ===3, 看來是三個setState共同作用后的結果,所以不是合并,看起來是這樣的。
在看例子??:
newState = (name) => {
console.log('pre:', this.state);
return {name,age: this.state.age+1};
}
click=()=>{
console.log('start!')
this.setState((preState)=>{
console.log('pre1:', preState)
return {name:'name1',age: preState.age+1}
})
this.setState((preState)=>{
console.log('pre2:', preState)
return {name:'name2',age: preState.age+1}
})
this.setState((preState)=>{
console.log('pre3:', preState)
return {name:'name3',age: preState.age+1}
})
console.log('end!')
};
最終打印:
start!
end!
pre1: {name: "rose", age: 0}
pre2: {name: "name1", age: 1}
pre3: {name: "name2", age: 2}
render! {name: "name3", age: 3}
總結
每次setState都執(zhí)行了,preState拿到了上次setState之后的結果,作為參數(shù)傳遞給下一個setState,從感覺上像是立馬更新了state。
其實還是:
把更改之后的state排入一個隊列,在要渲染的時候,批量處理,也就是拿隊列里面最后進來的那個state...
setState是異步的?
為什么說setState是異步的?
看個例子??【異步】:
this.setState({name:'jack', age:18})
console.log(this.state)
打印出來居然還是setState之前的值!
{name: "rose", age: 0}
其實是設置成功了的!只是因為異步,我們可以在setState的回調函數(shù)里面看到最新的值:
this.setState({name:'jack', age:18}, ()=>{
console.log(this.state)
})
但是setState就真的是異步了的嗎?
不全是!
再看個例子??【同步】:
componentDidMount() {
document.getElementById('btn').addEventListener('click',this.click)
}
click=()=>{
console.log('start')
this.setState({name:'jack', age:18})
console.log('Hello:', this.state)
console.log('end!')
}
render() {
console.log('render!', this.state)
return (
<button id='btn'>click me!</button>
)
}
打印結果:
start!
render! {name: "jack", age: 18}
Hello: {name: "jack", age: 18}
end!
執(zhí)行了setState之后立馬render,再執(zhí)行下一句代碼console.log('Hello:', this.state),所以,是同步的!
總結
setState在react自己的機制下是異步的,在原始機制下比如addEventListener原始事件機制下,setTimeout,promise等是同步的!
例子快照答案
- render打印了一次
- 最終結果是
{name: "name5", age: 2}