1. 程式人生 > >(十二)React-Native-生命週期詳解

(十二)React-Native-生命週期詳解

一.React-Native生命週期

說到生命週期,大家大概也能想到就是建立、銷燬、狀態改變。RN的元件就是一個狀態機。它接收兩個輸入引數:props和state,返回一個Virtual DOM。和Native一樣,RN也為我們提供相應的鉤子函式。RN的狀態變化取決於props和state。我們先來看一張經典圖。


這張圖涵蓋了一個元件從建立、執行到銷燬的整個過程。大家可以看到,初始化的時候會呼叫5個函式(按先後順序)。這5個函式在整個元件被建立到銷燬的過程中只調用一次。初始化完畢後,當元件的props或者state改變都會觸發不同的鉤子函式,繼而引發元件的重新渲染。現在我們把這過程拆開一點一點來分析。

初始化

我們先來看初始化,在初始化的過程中,會按順序呼叫下面5個函式。

getDefaultProps:元件例項建立前呼叫,多個例項間共享引用。注意:如果父元件傳遞過來的Props和你在該函式中定義的Props的key一樣,將會被覆蓋。

getInitalState:元件示例建立的時候呼叫的第一個函式。主要用於初始化state。注意:為了在使用中不出現空值,建議初始化state的時候儘可能給每一個可能用到的值都賦一個初始值。

componentWillMount:在render前,getInitalState之後呼叫。僅呼叫一次,可以用於改變state操作。

render:元件渲染函式,會返回一個Virtual DOM,只允許返回一個最外層容器元件。render函式儘量保持純淨,只渲染元件,不修改狀態,不執行副操作(比如計時器)。

componentDidMount:在render渲染之後,React會根據Virtual DOM來生成真實DOM,生成完畢後會呼叫該函式。在瀏覽器端(React),我們可以通過this.getDOMNode()來拿到相應的DOM節點。然而我們在RN中並用不到,在RN中主要在該函式中執行網路請求,定時器開啟等相關操作

下面我們來演示getDefaultProps初始化Props以及父元件覆蓋問題(AppConnect和Provider是和redux相關的程式碼,大家請跳過這一行):


比如我們在這裡定義了SimpleApp的預設Props為一個key為name,value為wsd的字典(ES6以後廢除了getDefaultProps而使用上述方式),然後我們在它的父元件App中傳入一個同樣key為name的Props,然後我們在SimpleApp中使用this.props.name把props打印出來,如下:


可以看到,原先的wsd被後面傳入的kingStart覆蓋了。

然後我們來看初始化State的演示(ES6裡使用constructor):


我們初始化一個state為key為sex,value為boy的state物件,然後我們在componentWillMount函式中改變已經初始化的sex和沒有宣告的age,最後在render中列印:


可以看到我們在render中打印出了state中兩個屬性的值。在這裡我們需要注意的是,如果在componentWillMount中直接修改state的值不會引發render的再次渲染。而如果把修改state的操作放到在render執行完之後的componentDidMount中,是會引發render的再次渲染的。

執行中

初始化完成之後,元件將會進入到執行中狀態,執行中狀態我們將會遇到如下幾個函式:

componentWillReceiveProps(nextProps):props改變(父容器來更改或是redux),將會呼叫該函式。新的props將會作為引數傳遞進來,老的props可以根據this.props來獲取。我們可以在該函式中對state作一些處理。注意:在該函式中更新state不會引起二次渲染。

boolean shouldComponentUpdate(object nextProps, object nextState):該函式傳遞過來兩個引數,新的state和新的props。state和props的改變都會調到該函式。該函式主要對傳遞過來的nextProps和nextState作判斷。如果返回true則重新渲染,如果返回false則不重新渲染。在某些特定條件下,我們可以根據傳遞過來的props和state來選擇更新或者不更新,從而提高效率。

componentWillUpdate(object nextProps, object nextState):與componentWillMount方法類似,元件上會接收到新的props或者state渲染之前,呼叫該方法。但是不可以在該方法中更新state和props。

render:跟初始化的時候功能一樣。

componentDidUpdate(object prevProps,object prevState):和初始化時期的componentDidMount類似,在render之後,真實DOM生成之後呼叫該函式。傳遞過來的是當前的props和state。在該函式中同樣可以使用this.getDOMNode()來拿到相應的DOM節點。如果你需要在執行中執行某些副操作,請在該函式中完成。

我們來演示componentWillReceiveProps的呼叫時機,對於頂層元件,我們新增一個文字及一個點選事件:


按鈕點選以後,我們將自身state的name屬性改變,並傳遞給SimpleApp(這裡的AppConnect就是SimpleApp),結果如下:


我們可以看到,第一次render,列印的是defaultProps傳過來的props。當按鈕點選,頂層元件state改變,引發頂層元件重新渲染,父元件傳遞的name發生改變,componentWillReceiveProps被呼叫,繼而引發二次渲染。在第二次render的時候,打印出來的就是新傳遞過來的props。

銷燬

銷燬階段只有一個函式,很簡單

componentWillUnmount:元件DOM中移除的時候呼叫。在這裡進行一些相關的銷燬操作,比如定時器,監聽等等。

為了加深記憶,我們把初始化和執行中所有的鉤子函式寫出來,讓大家看看最終的執行結果。


我們首先初始化元件,不執行任何操作,列印結果如圖所示:


當我們點選按鈕,改變元件的props之後,列印結果如下:


我們給自身元件添加了一個點選事件,點選之後改變自身的state,如下:


點選之後,再來看呼叫結果:


二.props和state

上面講完了生命週期,我們對props和state的不同點以及相同點作一個總結,加深大家理解。

相同點

1.不管是props還是state的改變,都會引發render的重新渲染。

2.都能由自身元件的相應初始化函式設定初始值。

不同點

1.初始值來源:state的初始值來自於自身的getInitalState(constructor)函式;props來自於父元件或者自身getDefaultProps(若key相同前者可覆蓋後者)。

2.修改方式:state只能在自身元件中setState,不能由父元件修改;props只能由父元件修改,不能在自身元件修改。

3.對子元件:props是一個父元件傳遞給子元件的資料流,這個資料流可以一直傳遞到子孫元件;state代表的是一個元件內部自身的狀態,只能在自身元件中存在。



'use strict'
import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    PixelRatio,
    AlertIOS,
    ActionSheetIOS,
    WebView,
} from 'react-native';
var  Dimensions = require('Dimensions');
var width = Dimensions.get('window').width;
var height = Dimensions.get('window').height;


class ReactiveOne  extends Component{
    constructor(props){
      super(props);
        console.log('constructor');
        this.state = {
            sex:'box',
        };
    }
    componentWillMount() {
        this.setState({
            age: '16',
        });
        console.log('componentWillMount');
    }
    componentDidMount() {
        console.log('componentDidMount');
        this.setState({
            sex:'girls',
            age:'20',
        });
    }
    render () {
        console.log('render');
        return (
            <View style={{backgroundColor:'red',width:width,height:height
}}></View>
        );
    }
    componentWillReceiveProps (nextProps) {

        console.log('conponentWillReceiveProps',nextProps);
    }
    shouldComponentUpdate() {
        console.log('shouldComponentUpdate');
        return true;
    }
    componentWillUpdate() {
        console.log('componentWillUpdate');
    }
    componentDidUpdate () {
        console.log('componentDidUpdate');
    }
}
AppRegistry.registerComponent('ReactiveOne', ()=>ReactiveOne);

2016-11-04 14:01:38.120 [info][tid:com.facebook.react.JavaScript] constructor

2016-11-04 14:01:38.120 [info][tid:com.facebook.react.JavaScript] componentWillMount

2016-11-04 14:01:38.120 [info][tid:com.facebook.react.JavaScript] render

2016-11-04 14:01:38.124 [info][tid:com.facebook.react.JavaScript] componentDidMount

2016-11-04 14:01:38.125 [info][tid:com.facebook.react.JavaScript] shouldComponentUpdate

2016-11-04 14:01:38.125 [info][tid:com.facebook.react.JavaScript] componentWillUpdate

2016-11-04 14:01:38.126 [info][tid:com.facebook.react.JavaScript] render

2016-11-04 14:01:38.131 [info][tid:com.facebook.react.JavaScript] componentDidUpdate

這裡特別注意: shouldComponentUpdate  函式的返回值是一個bool的 true 表示允許允許應用進行屬性值的修改,執行後續的操作,false 表示不允許執行後續的操作

如果不實現shouldComponentUpdate 該方法,則走系統的預設的去返回true或者false ;

如果使用者實現了shouldComponentUpdate 該方法,則應用走使用者的結果 如果無返回值則報下面的錯誤:

2016-11-04 14:01:00.221 [warn][tid:com.facebook.react.JavaScript] Warning: ReactiveOne.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.