??react可以通過this.state.xx的方式直接獲取state,但是當(dāng)我們修改state的時候,往往有許多的坑。
- 不能直接修改state
組件修改state,并不會重新觸發(fā)render。例如:
//錯誤
this.state.title='attend';
//正確
this.setState({title:'attend'});
- state的更新是異步的
??調(diào)用setState時,組件state并不會立即改變,只是把要修改的狀態(tài)放入事件隊列當(dāng)中,react會優(yōu)化真正的執(zhí)行時機,如果連續(xù)寫多次setState,會將多次setState的狀態(tài)修改合并成一次狀態(tài)修改。
//正確
this.setState((prevState, props)=>({
counter: prevState.counter + 1
}))
- state的更新是一個合并的過程
??當(dāng)調(diào)用setState()修改組件的狀態(tài)時,只需要傳入發(fā)生改變的state,而不是完整的state,因為組件state的更新是一個合并的過程:
this.state = {
title: 'React',
content: 'React is an wondeful JS library'
}
??當(dāng)只需要修改title時,只需要將修改的title傳給setState即可:
this.setState({title:'ReactJs'});
??react會合并最新的title到原來的狀態(tài),同時保留原來狀態(tài)的content,最終合并state為:
this.state = {
title: 'ReactJs',
content: 'React is an wondeful Js library'
}
state與不可變對象
??react官方把state當(dāng)成不可變對象,一方面直接修改this.state,組件并不會重新render;另一方面,state中包含的所有狀態(tài)都應(yīng)該是不可變的對象,state當(dāng)中的某一個狀態(tài)發(fā)生變化時,應(yīng)該重新創(chuàng)建這個狀態(tài)對象,而不是直接修改原來的state狀態(tài),那么當(dāng)狀態(tài)發(fā)生變化時,如何去創(chuàng)建新的狀態(tài)呢,我們根據(jù)狀態(tài)類型可以分為下面三種情況:
- 狀態(tài)類型為不可變類型
number、string、boolean、null、undefined
這種情況最簡單,因為狀態(tài)是不可變類型,所以直接給要修改的狀態(tài)賦一個新值即可,例如我們要修改的count為number型,title(string),success(boolean)三個狀態(tài):
this.setState({
count:1,
title:'React',
success:true
})
- 狀態(tài)類型為數(shù)組
假如有一個數(shù)組類型的狀態(tài)books,當(dāng)向books中增加一本書時。
//方法一:使用preState,concat創(chuàng)建新數(shù)組
this.setState((prevState)=>({
books: prevState.books.concat(['React Guide'])
}))
//方法二:ES6 spread syntax
this.setState(prevState=>({
books:[...prevState,'React Guide']
}))
當(dāng)我們從books中截取部分元素作為新狀態(tài)時,可以用數(shù)組的slice方法:
this.setState(prevState=>({
books: prevState.books.slice(1,3);
}))
當(dāng)從books中過濾部分元素后,作為新狀態(tài)時,可以使用filter方法:
this.setState(prevState=>({
books: prevState.books.filter(item=>{
return item!='React';
})
}))
【注意】不要使用push,pop,shift,unshift,splice等方法修改數(shù)組類型的狀態(tài),因為這些方法都是在原數(shù)組的基礎(chǔ)上修改的,而concat,slice,filter會返回一個新的數(shù)組。
- 狀態(tài)的類型是普通對象
使用es6的Object.assgin()方法
this.setState({
onwer: Object.assgin({},preState.onwer,{name:'Jason'});
})
(2) 使用對象擴展語法(Object spread properties):
this.setState(preState=>{
owner: {...preState.owner, name:'Jason'}
})
總結(jié)
??創(chuàng)建新的狀態(tài)的關(guān)鍵是,避免使用直接修改原對象的方法,這種方法在vue中稱為變異方法,而是使用可以返回一個新對象的方法,當(dāng)然可以使用Immutable的JS庫(Immutable.js)實現(xiàn)類似的效果。
思考
為什么React推薦組件狀態(tài)的修改時不可變對象呢?
(1) 不可變對象的修改會返回一個新的對象,不用擔(dān)心原對象在不小心的情況下修改導(dǎo)致的錯誤,方便程序的管理和調(diào)試。
(2) 處于性能的考慮,對象組件的狀態(tài)時不可變對象時,在組件的shouldComponentUpdate方法中僅需要比較前后兩次狀態(tài)對象的引用就可以判斷狀態(tài)是否真的改變,從而避免不必要的render調(diào)用。
進(jìn)階
??除了以上方法改變react組件的狀態(tài)之外,我們還經(jīng)常會用到replaceState()改變組件的狀態(tài)。
??replaceState()方法與setState()類似,但是方法只會保留nextState中狀態(tài),原state不在nextState中的狀態(tài)都會被刪除。
//使用語法:
replaceState(object nextState[, function callback])
nextState,將要設(shè)置的新狀態(tài),該狀態(tài)會替換當(dāng)前的state。
callback,可選參數(shù),回調(diào)函數(shù)。該函數(shù)會在replaceState設(shè)置成功,且組件重新渲染后調(diào)用。