( _(:3 」∠)_給園友們提個建議,不管是API文檔仍是書籍,必定要多看幾遍!特別是隔一段時間後,會有意想不到的收穫的)
這篇文章主要是寫關於學習react中的一些本身的思考:
1.setState究竟是同步的仍是異步的?
2.如何在子組件中改變父組件的state
3.context的運用,避免「props傳遞地獄」
4.組件類裏有私有變量a,它到底改放在this.a中仍是this.state對象中(做爲屬性a)呢?
setState究竟是同步的仍是異步的?
class MyComponent extends React.Component{
constructor(props) {
super(props)
this.state ={
value:0
}
}
handleClick = () => {
this.setState({value:1})
console.log('在handleClick裏輸出' + this.state.value);
}
render(){
console.log('在render()裏輸出' + this.state.value);
return (<div>
<button onClick ={this.handleClick}>按鈕</button>
</div>)
}
}
export default MyComponent
//省略渲染過程,下面也同樣
在這裏咱們點擊按鈕時,調用handleClick函數,首先調用this.setState()設置value,隨即把this.state.value輸出,結果是什麼?
你可能會想,這還不簡單——「在handleClick裏輸出1」唄,然而你錯了,它的結果爲:
事實上,setState()的調用大多數時候是異步的,這意味着,雖然你調用了setState({value:0}),但this.state.value並不會立刻變成0,而是直到render()函數調用時,setState()才真正被執行。結合圖說明一下:
你可能又會問了:要是我在render()前屢次調用this.setState()改變同一個值呢?(好比value)
咱們對handleClick作一些修改,讓它變得複雜一點,在調用handleClick的時候,依次調用handleStateChange1 ,handleStateChange2,handleStateChange3,它們會調用setState分別設置value爲1,2,3而且隨即打印
handleStateChange1 = () => {
this.setState({value:1})
console.log('在handleClick裏輸出' + this.state.value);
}
handleStateChange2 = () => {
this.setState({value:2})
console.log('在handleClick裏輸出' + this.state.value);
}
handleStateChange3 = () => {
this.setState({value:3})
console.log('在handleClick裏輸出' + this.state.value);
}
handleClick = () => {
this.handleStateChange1();
this.handleStateChange2();
this.handleStateChange3();
}
那麼輸出結果會是什麼呢?若是setState老是同步調用的,那麼結果顯然爲
在handleClick裏輸出1
在handleClick裏輸出2
在handleClick裏輸出3
可是結果爲:,證實它可能是異步的
這下好理解了吧,配合這幅圖:
setSate大部分的時候是異步執行的,可是,在react自己監聽不到的地方,如原生js的監聽裏,setInterval,setTimeout裏,setState就是同步更新的html
關於更多React的異步同步問題請點擊這裏react
如何在子組件中改變父組件的state?
這是咱們常常會遇到的問題之一,解決辦法是:在父組件中寫一個能改變父組件state的方法,並經過props傳入子組件中
class Son extends React.Component{
render(){
return(<div onClick = {this.props.handleClick}>
{this.props.value}
</div>)
}
}
class Father extends React.Component{
constructor(props){
super(props)
this.state ={
value:'a'
}
}
handleClick = () => {
this.setState({value:'b'})
}
render(){
return (<div style ={{margin:50}}>
<Son value = {this.state.value} handleClick = {this.handleClick}/>
</div>)
}
}
點擊子組件Son,內容由a變成b,說明父組件的state被修改了
context的運用,避免「props傳遞地獄」
3.1假設一個比較極端的場景:你須要從你的子組件裏調用父父父父父組件的屬性或方法,怎麼辦!當組件嵌套層級過深的時候,不斷地傳props做爲實現方式簡直就是噩夢!我稱之爲「props傳遞地獄」(這個詞是我瞎編的,參考自「回調函數地獄」)
咱們接下來實現的是這樣一個需求,把gene屬性(基因)從組件GrandFather -->Father --> Son傳遞,若是用props傳遞:
class Son extends React.Component{
render(){
return (<h3 style ={{marginTop:30}}>我從個人爺爺那裏獲得了基因--{this.props.gene}</h3>)
}
}
class Father extends React.Component{
render(){
return (<Son gene = {this.props.gene}/>)
}
}
class GrandFather extends React.Component{
constructor(props) {
super(props)
this.state ={
gene:'[爺爺的基因]'
}
}
render(){
return (<Father gene = {this.state.gene}/>)
}
}
demo:
【(。・`ω´・)雖然聽起來有點怪怪的可是你們別介意哈】
實現是實現了,但你想一想,假設不是從「爺爺」組件,而是從「太太太太爺爺」組件傳下來,這多可怕!不過不要緊,react提供了一個叫作context(上下文)的API,你在頂層組件的context中定義的屬性,能夠在全部的後代組件中,經過this.context.屬性去引用!讓咱們一睹爲快:
class Son extends React.Component{
render(){
console.log(this.context.color);
return (<h3 style ={{marginTop:30}}>我從個人爺爺那裏獲得了基因--{this.context.gene}</h3>)
}
}
Son.contextTypes ={
gene:React.PropTypes.string
}
class Father extends React.Component{
render(){
return (<Son/>)
}
}
class GrandFather extends React.Component{
getChildContext(){
return {gene:'[爺爺的基因]'}
}
render(){
return (<Father />)
}
}
GrandFather.childContextTypes = {
gene: React.PropTypes.string
};
export default GrandFather
demo效果同上!這個時候你發現,咱們在<GrandFather>組件和<Father>組件中都沒有向下傳遞props,咱們就從最下層的Son組件中獲取了gene屬性,是否是很方便!
解釋下代碼:
getChildContext()是你在頂層組件中定義的鉤子函數,這個函數返回一個對象——你但願在後代組件中取用的屬性就放在這個對象中,譬如這個例子中我但願在Son組件中經過this.context.gene取屬性,因此在getChildContext()中返回{gene:'[爺爺的基因]'}
GrandFather.childContextTypes和Son.contextTypes 用於規定頂層組件和取頂層組件context的後代組件的屬性類型
【注意】GrandFather.childContextTypes和Son.contextTypes 這兩個對象必需要規定!不然context只能取到空對象!一開始我犯的這個錯誤簡直讓我狂吐三升血。。。。
有圖有真相之context和props的區別
雖然上面這個例子說明了context多麼好用,但注意:官方並不推薦常用它,由於它會讓你的應用架構變得不穩定(官方文檔原話If you want your application to be stable, don't use context),在我看來,爲何在大多數狀況下要使用props而不是實現數據流呢,由於props憑藉組件和組件間嚴密的邏輯聯繫,使得你可以清晰地跟蹤應用的數據流(it's easy to track the flow of data through your React components with props)固然了,若是你遇到上述的例子的狀況,context仍是大有裨益的
3.3須要改變context中的屬性時候,不要直接改變它,而是使用this.state做爲媒介,若是你試圖在頂層組件的state中放入一個可變的屬性你能夠這樣作:
getChildContext(){
return {type:this.state.type}
}
3.4在上述我限制gene的類型時候我是這樣寫的:gene: React.PropTypes.string,使用了React內置的React.PropTypes幫助屬性,此時個人版本爲 "react": "15.4.2",在15.5的版本後這一幫助屬性被廢棄,推薦使用props-types庫,像這樣:
const PropTypes = require("Prop-Types");
GrandFather.childContextTypes = {
gene: PropTypes.string
};
固然,在這以前你須要npm install prop-types
組件類裏有私有變量a,它到底改放在this.a中仍是this.state對象中(做爲屬性a)呢?
這得根據它是否須要實時的重渲染決定,若是該變量須要同步到變化的UI中,你應該把它放在this.state對象中,若是不須要的話,則把它放在this中
(無代碼無demo)
【完】--喜歡這篇文章的話不妨關注一下我喲