react state 生命週期詳解 props-type Children

不能直接修改state

//錯誤
this.state.title='React';
正確修改方式是使用setState();
//正確
this.setState({title:'React'});

也可以使用另一個函數作爲參數的setState,這個函數有兩個參數,第一個參數是當前的最新狀態(本次組件狀態更新後的狀態)的前一個狀態preState(本次組件狀態修改前的狀態),第二個參數是當前最新的props。示列如下:

this.setState((preState,props)=>({
    counter:preState.quantity+1
}))

setState方法有兩個參數  第一個參數可以是一個函數  也可以是一個對象  。第二個參數是修改了state狀態的時候的回調函數

第一個參數如果是對象的話  是爲了提供更加簡潔的方式直接替換某個變量的值

第一個參數爲函數適用於提供精確的上下文,來更新對之前狀態的有依賴的操作

同時setState不保證同步的更新機制  如果在一個事件裏面多次修改某一個值  爲了性能會使用批量更新機制 纔去隊列的方式執行

setState 執行只有是在react事件裏面纔是異步的   其他時候都是同步的    ,

//修改msg的值是異步的
onChange = ()=>{
    this.setState({
        msg:111
    })
}
//放在定時器裏面   修改msg的值就是同步的了
onChange = ()=>{
    setTimeout(function(){
         this.setState({
            msg:111
        })
    },1000)
}


function increment(prevState,props){
    return {count: prevState.count + 1};
}

function incrementMultiple(){
    this.setState(increment);
    this.setState(increment);   //函數方式
    this.setState({count: this.state.count + 1});   //對象方式
    this.setState(increment);
}

state  狀態類型爲不可變類型(number,string,bool,null,undefined)這種情況最簡單,因爲狀態時不可變類型,所以直接給要修改的狀態賦一個新值即可

state  狀態類型爲數組

//方法一:使用preState,concat創建新數組
this.setState((preState)=>({
    books:preState.books.concat(['React Guide'])
}))
//方法二:ES6 spread syntax
this.setState(preState=>({
    books:[...preState,''React Guide]
}))
當我們從books中截取部分元素作爲新狀態時,可以用數組的slice方法:
this.setState(preState=>({
    books:preState.books.slice(1,3);
}))
當從books中過濾部分元素後,作爲新狀態時,可以使用filter方法:
this.setState(preState=>({
    books:preState.books.filter(item=>{
        return item!='React';
    })
}))

注意:不要使用push,pop,shift,unshift,splice登方法修改數組類型的狀態,因爲這些方法都是在原數組的基礎上修改的,而concat,slice,filter會返回一個新的數組。

state  狀態的類型是普通對象(不包含:string,array)

//使用es6的Object.assgin()方法
this.setState({
    onwer:Object.assgin({},preState.onwer,{name:'Jason'});
})
//使用對象擴展語法(Object spread properties):
this.setState(preState=>{
    owner:{...preState.owner,name:'Jason'}
})

replaceState()方法與setState()類似,但是方法只會保留nextState中狀態,原state不在nextState中的狀態都會被刪除

默認調用setState都會重新渲染視圖,但是通過shouldComponentUpdate()函數返回false來避免重新渲染。

沒有導致state的值發生變化的setState是否會導致重渲染 ——【會!】

handleClick = () => {
     const preNumber = this.state.Number
     this.setState({
        Number:this.state.Number
     })
  }
 render(){
    //當render函數被調用時,打印當前的Number
    console.log(this.state.Number)
    return(<h1 onClick = {this.handleClick} style ={{margin:30}}>
             {this.state.Number}
           </h1>)
  }

上面的代碼  當點擊函數執行的時候  會重複的執行render函數重新渲染模板,下面是解決辦法

//添加一個生名週期的鉤子函數 
shouldComponentUpdate(nextProps,nextState){
      if(nextState.Number == this.state.Number){
        return false
      }
  }

PS:前後不改變state值的setState(理論上)和無數據交換的父組件的重渲染都會導致組件的重渲染,但你可以在shouldComponentUpdate這道兩者必經的關口阻止這種浪費性能的行爲

 

生命週期

組件觸發的時候  氛圍下面幾個階段

1:初始化(創建組件實例)(constructor)

2:掛載(組件實例和DOM產生關聯)(componentWillMount   render   componentDidMount)

3:運行時(state,props發生變化)(componentWillReceiveProps(nextProps),

               shouldComponentUpdate(nextProps,nextState),

               componentWillUpdate(),

               render(),

               componentDidUpdate()),

4:銷燬(元素失效)componentWillUnmount()

    componentWillMount(){
		console.log('componentWillMount');
	}
	
	componentDidMount(){
		console.log('componentDidMount');
	}
	
	render(){
		console.log('render');
		let loginFlag = this.state.Loginflag;
		if(loginFlag){
			return <Redirect to={{pathname:'/'}} />
		}
		return (
			<div>
				<Login_bak name={this.state.msg} />
				{loginFlag ? "111" : "222"}
				<button onClick={this.doLogin.bind(this)}>登入</button>
			</div>
		)
	}

    //加載組件的時候輸出順序 componentWillMount  render  componentDidMount  
    //也就是表示會先執行componentWillMount  再去render  最後  componentDidMount  
    //如果render函數裏面有子組件    那麼在執行render函數的時候   會先去加載子組件的內容,加載順序也是一樣的

 

ComponentWillMount  組件即將被調用  只執行一次

      不能操作setState修改狀態

       不能操作DOM   因爲還沒有掛載DOM   

       和constructor  的作用差不多    不建議在該鉤子中加載後臺數據

ComponentDidMount   組件完成渲染  只執行一次

       可以操作setState修改狀態

       可以操作DOM   

       可以在該鉤子中加載後臺數據

ComponentWillReceiveProps  props值更新的時候執行   根據props觸發內部狀態轉換

       可以操作setState修改狀態

       可以操作DOM 

        和當前的props進行比對  有可能什麼也沒有發生

shouldComponentUpdate(nextProps,nextState)  props和state更新時候觸發

       不能操作setState修改狀態   會導致死循環

       可以操作DOM   但是建議不要

ComponentWillUpdate  組件運行時  再次渲染前

      不能操作setState

       可以操作DOM   但是建議不要

        基本用不上該鉤子函數

ComponentDidUpdate()  組件運行時  再次渲染

       可以操作setState

       可以操作DOM  

        基本用不上該鉤子函數

ComponentWillUnmount()  組件銷燬時執行

       不能操作setState  即使設置了也沒有用

       可以操作DOM  

        基本用不上該鉤子函數

 

 

關於props-type  類型檢測

使用之前必須下載引入

   import PropTypes from 'prop-types'

//  propTypes能用來檢測全部數據類型的變量,包括基本類型的的字符串,布爾值,數字,
以及引用類型的對象,數組,函數,甚至還有ES6新增的符號類型
static propTypes = {
     optionalArray: PropTypes.array,//檢測數組類型
     optionalBool: PropTypes.bool,//檢測布爾類型
     optionalFunc: PropTypes.func,//檢測函數(Function類型)
     optionalNumber: PropTypes.number,//檢測數字
     optionalObject: PropTypes.object,//檢測對象
     optionalString: PropTypes.string,//檢測字符串
     optionalSymbol: PropTypes.symbol,//ES6新增的symbol類型
}
//  propTypes類型檢測的缺憾之一是,對於不確定的和無效的值,它無法捕捉錯誤
如果檢測類型錯誤的話   控制檯會拋出錯誤   但是如果傳入的是undefined或者null的話   propTypes就無法檢測到錯誤了

通過oneOfType實現多選擇檢測-可規定多個檢測通過的數據類型

上個例子中類型檢測的要求是一個變量對應一個數據類型,也就是規定的變量類型只有一個。那麼怎樣能讓它變得靈活一些,比如規定多個可選的數據類型都爲檢測通過呢? PropTypes裏的oneOfType方法可以做到這一點,oneOfType方法接收參數的是一個數組,數組元素是你希望檢測通過的數據類型。

static propTypes = {
    number:PropTypes.oneOfType(
        [PropTypes.string,PropTypes.number]
    )
}

這時候,因爲在類型檢測中,號碼屬性的規定類型包括字符串和數字兩種,所以此時控制檯無報錯
當然,如果你改爲number = {數組或其他類型的變量},那麼這時就會報錯了

 通過oneOf實現多選擇檢測-可規定多個檢測通過的變量的值

static  propTypes = {
    number:PropTypes.oneOf(
          [12,13]
      )
}

//規定props的值  必須是其中之一

arrayOf,objectOf實現多重嵌套檢測

static propTypes = {
     array:PropTypes.arrayOf(PropTypes.number)
}

<Son array = {[1,2,3,4]}/>
//上面的代碼是可以正常渲染的
然後我們把<Son array = {[1,2,3,4]} />改爲<Son array = {['1','2','3','4']} /> ,
這樣雖然也會報錯但是還是可以正常渲染。報錯是因爲類型檢測的不是非致命性錯誤,
不會導致頁面的渲染失敗

通過形狀方法檢測目標對象不同屬性的不同數據類型

objectOf有一個缺陷,就是它內部的屬性的數據類型被強行規定爲一種,但通常一個對象裏應該是有多種不同類型的屬性了,那麼這時候objectOf就不符合要求了,我們應該使用形狀方法

static propTypes = {
     object:PropTypes.shape({
         name:PropTypes.string,
         age:PropTypes.number
     })
}
<Son object = {{name:'彭湖灣',age:20}}/>
//上面的代碼是可以正常渲染的 
把<Son object = {{name:'彭湖灣',年齡:20}} />改成
   <Son object = {{name:'彭湖灣',年齡:'20'}} /> ,
然後就能喜聞樂見得報錯了

通過isRequired檢測道具中某個必要的屬性(如果該屬性不存在就報錯)

有時候,我們在對某個變量進行類型檢測時,我們不僅要求它符合預期的類型,同時也要求它是必須寫入的,這時候就要用到isRequired。

static propTypes = {
          number:PropTypes.number
}
這段代碼的作用是當你在道具中寫入號碼屬性且號碼屬性類型錯誤時給予報錯提示,
可如果你壓根就沒有寫入號碼屬性呢?沒錯,什麼錯誤都不會報。
這就是使用isRequired的必要性

修改一下
static propTypes = {
    number:PropTypes.number.isRequired
}
如果不給組件傳遞number參數  就會報錯了

【注意】在這裏給大家提個問題:我們上述的寫法是數量:PropTypes.number.isRequired,
這要求數是數字類型,但如果你不想控制數的類型而僅僅是想控制它的必要性呢?\
難道寫成號:isRequired或number:PropTypes.isRequired? 
這個時候PropTypes.any就登場啦!它代表了該變量可取任何一種數據類型,
所以你可以寫成這樣--number:PropTypes.any.isRequired

應對更復雜的類型檢測 - 將PropTypes的屬性值寫成函數

Son.propTypes = {
     email:function(props,propName,componentName){
            if(!/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/.test(props[propName])){
                  return new Error('組件' + componentName+ '裏的屬性' + propName + '不符合郵箱的格式');
                         }
                }
}

<Son email = {2314838004}/>
這樣就會拋出來自定義的錯誤了

Children

this.props.children 的值有三種可能:如果當前組件沒有子節點,它就是 undefined ;如果有一個子節點,數據類型是 object ;如果有多個子節點,數據類型就是 array 。所以,處理 this.props.children 的時候要小心。

React中的Children不一定是組件,它們可以使任何東西。

JSX將會自動刪除每行開頭和結尾的空格,以及空行。它還會把字符串中間的空白行壓縮爲一個空格。這意味着以下的這些例子都會渲染出一樣的情況:

<Grid>Hello world!</Grid>

<Grid>
  Hello world!
</Grid>

<Grid>
  Hello
  world!
</Grid>

<Grid>

  Hello world!
</Grid>

操作children

props.children 可以使任何的類型,比如數組、函數、對象等等。

循環Children的方式有兩種    react.children  && this.props.children  分別是兩種創造數組或者對方的方式

react.children  下面有多種方法   forEach  map  count   only toArray

this.props.children  如果接受的本身就是數組的話    那麼就可以直接使用原生js的數組API了

當children只是一個函數的話   那麼使用forEach  map的時候就會報錯了   所以children最少應該有兩個子組件的方式,但是確實很難知道到底有多少個子組件傳過來了,所以可以使用  React.Children.count(this.props.children)  來檢測子組件的個數 ,無論是什麼類型 都可以檢測出子組件的個數了 。記住一點不可以使用this.props.children.length來檢測子組件的個數  ,因爲如果子組件只有一個  而且是個字符串   這時候檢測就失敗了

如果以上的方法你都不適合,你能將children轉換爲數組通過 React.Children.toArray 方法。如果你需要對它們進行排序,這個方法是非常有用的    const children = React.Children.toArray(this.props.children)  轉化了之後  就可以直接使用forEach  map方式來實現遍歷操作了

執行單一child

如果你的組件,它只能在傳遞單一child的情況下使用,而且child必須爲函數。

class Executioner extends React.Component {
  render() {
    return this.props.children()
  }
}

//如果是單一組件而且是個函數的話   那就只能這麼去調用子組件了

這時候我們可以試着去強制執行 propTypes ,就像下面這樣
Executioner.propTypes = {
  children: React.PropTypes.func.isRequired,
}

這會使控制檯打印出一條消息,部分的開發者將會把它忽視。相反的,我們可以使用在 render 裏面使用 React.Children.only
class Executioner extends React.Component {
  render() {
    return React.Children.only(this.props.children)()
  }
}

這樣只會返回一個child。如果不止一個child,它就會拋出錯誤,讓整個程序陷入中斷——完美的避開了試圖破壞組件的懶惰的開發者。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章