React的生命週期與應用

傳播正能量發表於2016-10-31

目錄

  • 1.react元件的兩種建立寫法

  • 2.元件的生命週期在不同狀態下的執行順序

  • 3.元件各生命週期的應用

1.react元件的兩種建立寫法

第一種ES5寫法,React.createClass

React.createClass({
    getDefaultProps() {
        return {
            key1:value1
        }
    },
    getInitialState() {
        return {
            state1:state1
        }
    }
});

第二種是ES6寫法,繼承React.Component類

export default class Test1 extends React.Component {
    constructor (props) {
        super(props);
        this.state = {
            state1: state1
        }
    }
    static defaultProps = {
        data: 2,
    };
    static propTypes = {
        optionsData: React.PropTypes.array,
        onSelect: React.PropTypes.func,
        selectedOption: React.PropTypes.object,
        topStyle: React.PropTypes.any,
        placeholder: React.PropTypes.string
    }
 
}

getDefaultProps、getInitialState是在createClass時呼叫的方法,而在繼承component類時,getDefaultProps方法對應給類新增靜態屬性 defaultProps ,getInitialState 對應在類的建構函式中設定 state屬性

2.元件的生命週期在不同狀態下的執行順序

元件首次裝載(first-Mount):

  • getDefaultProps() →

  • getInitialState() →

  • componentWillMount() →

  • render() →

  • componentDidMount()

解除安裝元件時(Unmount):componentWillUnmount()

當重新裝載元件時(Re-Mount):

  • getInitialState()→

  • componentWillMount()→

  • render() →

  • componentDidMount(),

  • 但並不執行 getDefaultProps; defaultProps是放在元件類上的static屬性

當再次渲染元件時(Re-Render),此時按順序執行

  • componentWillReceiveProps(nextProps )(元件接收到新的props才會呼叫,只是改變state時不呼叫)→

  • shouldComponentUpdate(元件接收到新的props才會呼叫,只是改變state時不呼叫)→

  • componentWillUpdate→

  • render →

  • componentDidUpdate。

單獨呼叫setState的重新渲染

  • componentWillUpdate→

  • render →

  • componentDidUpdate

1、在單頁應用中,用react-router的history做頁面跳轉時,是將當前route的所有元件解除安裝,再跳轉回來時是重新裝載元件,而不是首次裝載。

2、在使用react-native的Navigator時,每次push新頁面的時候是首次載入,舊頁面沒有解除安裝,在pop新頁面的時候,新頁面會解除安裝 呼叫Unmount,舊頁面是重新渲染

  • componentWillReceiveProps→

  • componentWillUpdate→

  • render →

  • componentDidUpdate。
    ,不是重新裝載,也沒有重新渲染的shouldComponentUpdate控制,所以pop回來肯定重新渲染。

3、元件在記憶體中裝載過一次之後,元件的defaultProps就初始化了,之後裝載就不會重新設定。

4、父元件的render都會引起子元件的重新渲染。

5、 不能在componentWillUpdate ,render和componentDidUpdate 中呼叫setState

3.元件各生命週期的應用

3.1 getDefaultProps方法 或者 defaultProps 物件

  • 只在元件類建立的時候呼叫一次,之後會被快取起來。

  • defaultProps在元件例項之前建立,例項間共享。

  • 會和父元件設定的props進行合併。

3.2 getInitialState方法或constructor的state屬性

專案中會把元件用到的state初始化在這裡

constructor (props) {
    super(props);
    this.state = {
        poi: null,
        activeTabId: null,
        cartCount: Shopcart.size(),
        domSize:{
            headerHeight: 100,
            bannerHeight: 200,
            categoryTabHeight: 100,
        },
        hiddenBanner: false //是否隱藏banner
    };

}

3.3 componentWillMount()

元件初次render之前呼叫,如果在此方法中呼叫setState,render將感知到更新後的state,並且只執行這一次render,可以在此方法中fetch資料,不需要dom操作的資料獲取。

3.4 render()

元件渲染的方法,是元件唯一的必須實現的方法,在該方法中,我們可以通過props和state渲染不同的元件。返回null或者false代表不渲染任何東西。

render () {
    return (
        <header className="header-wrapper">
            <div className="header header-normal">
                {this.renderLeftComponent()}
                <span>{this.props.title || `美團商超`}</span>
                {this.renderRightComponent()}
            </div>
        </header>
    );
}

3.5 componentDidMount()

元件裝載後呼叫的方法,因為該方法呼叫的時候,元件已經裝載,並且該方法不在render的迴圈當中,一般在該方法中做一些fetch資料或者改變state的方法。
還可以通過ReactDOM.findDOMNode(_this.refs.wrapper) 來獲取DOM節點 進行操作。

componentDidMount() {
    this.mounted = true;
    if(this.props.poi){
        this.fetchCategoryTabs(this.props.poi.poiId);
    }
    if(!this.isCalculate) {
        this.calculateWidth(); 
    }
}

3.6 componentWillReceiveProps(nextProps)

在元件接收到新的 props 的時候呼叫。在初始化渲染的時候,該方法不會呼叫。可以在該方法中判斷,當props變化時,是否再去重新fetch資料,setState。

componentWillReceiveProps (nextProps) {
    if(nextProps.poi &&(nextProps.poi != this.props.poi)) {
        this.fetchBannerList(nextProps.poi.poiId);
    }
}

3.7 shouldComponentUpdate(nextProps, nextState)

在接收到新的props或者state變化時,被呼叫,該方法在初始化渲染和forceUpdate的時候不會被呼叫。

 預設返回true,如果返回false,則render不會執行。可以在這個方法中來阻止不必要的render,因為有時是因為父元件的render引起的子元件不必要的render。
shouldComponentUpdate(nextProps, nextState) {
    const isStateChanged = Object.keys(nextState).some(key=> {
        return nextState[key] !== this.state[key]
    });
    const isPropsChanged = Object.keys(nextProps).some(key=> {
        return nextProps[key] !== this.props[key]
    });
    return isStateChanged || isPropsChanged
}

3.8 componentWillUpdate(nextProps, nextState)

在接收到新的 props 或者 state 之前立刻呼叫。在初始化渲染的時候該方法不會被呼叫。使用該方法做一些更新之前的準備工作。你不能在剛方法中使用 this.setState()。如果需要更新 state 來響應某個 prop 的改變,請使用 componentWillReceiveProps。 專案中應用不多。

3.9 componentDidUpdate

在元件的更新已經同步到 DOM 中之後立刻被呼叫。該方法不會在初始化渲染的時候呼叫。使用該方法可以在元件更新之後操作 DOM 元素。

有些操作可能需要操作DOM元素,並且在第一次componentDidMount時還沒有達到條件,所以需要在componentDidUpdate時再做操作,但是componentDidUpdate在render的迴圈函式中,
所以需要設定變數做控制。

下面例子中 this.isCalculate 就是判斷是否計算過的變數。

componentDidMount() {
    this.mounted = true;
    if(this.props.poi){
        this.fetchCategoryTabs(this.props.poi.poiId);
    }
    if(!this.isCalculate) {
        this.calculateWidth(); 
    }
}
componentDidUpdate () {
    if(!this.isCalculate) {
        this.calculateWidth(); 
    }
}
calculateWidth () {
    if(this.isCalculate) {
        return;
    }
    let tablist = this.state.categoryTabs;
    if(tablist.length == 0) {
        return;
    }
    let tabsDOM = this.refs.tablist,
        childrensDOM = tabsDOM.childNodes,
        container = this.refs.tabcontainer,
        wrapper = this.refs.wrapper,
    // 橫向滾動寬度
        scrollwidth = 5;
    for(let i=0; i<childrensDOM.length; i++){
        let childDOM = childrensDOM[i];
        scrollwidth += childDOM.clientWidth + parseInt(childDOM.style.marginRight);
    }
    scrollwidth = Math.max(tabsDOM.clientWidth,scrollwidth);
    this.setState({tabsWidth: scrollwidth + `px`});
     
    this.props.setCategoryTabHeight(wrapper.offsetHeight);
    this.isCalculate = true;
}

3.10 componentWillUnmount

在元件從 DOM 中移除的時候立刻被呼叫。在該方法中執行任何必要的清理,比如無效的定時器,或者清除在 componentDidMount 中建立的 DOM 元素。
可以記錄元件的mount狀態,在 componentDidMount 中設定this.mounted = true 。 在componentWillUnmount 中設定 this.mounted = false。

相關文章