ReactNative組件詳解 自定義組件 組件的使用 狀態和屬性 生命週期 生命週期回調順序

ReactNative學習記錄傳送門

ReactNative核心思想就是組件化,它基於前端框架React,在我們使用其開發Android和iOS的時候,共用一套組件即一套代碼,增加了代碼複用性。今天的這篇文章不不分析過多的知識點,主要介紹如下內容:

  • 如何進行自定義組件
  • 如何使用自定義組件
  • 組件的生命週期

自定義組件

ReactNative中我們實現的UI都是有組件組成的,但是有時候爲了實現我們想要的效果,並且達到重用代碼的目的,往往需要我們自定義組件,那麼此時我們就需要對自定義組件的套路有一些見解。需要注意的是本篇文章是基於ES6的,但是若寫法與es5有差別的地方也會順帶提一下。

由於自定義組件是也是由基本的組件經過一些業務的處理組裝而成,那我們我們首先要了解ReactNative中基本的組件都有哪些,可前去官網查看.今天我們要實現的組件就是一個購物車數量加減功能。全文也圍繞這個小栗子展開介紹,效果如下


要實現這樣的一個組件,我們首先要知道Component,當我們創建組件的時候必須要繼承Component(ES5是使用React.createClass創建組件),而上圖的組件我們使用最基礎的組件Text來實現,就是三個Text在View裏橫向放置。在ReactNative開發時,如果我們使用基本組件或者其自定義的組件時,首先要做的工作就是導入,在ES6中使用關鍵字import來實現這一功能。導入基本組件Text,View以及組件類Component,當然爲了讀寫代碼方便,我們將樣式統一管理,則需要用到StyleSheet來創建樣式。則導入部分

import React, {Component} from 'react';
import {
    Text,
    View,
    StyleSheet
} from 'react-native';

上面是ES6s實現導入的,在最近幾天的學習中發現很多代碼依然是ES5的方式,那麼我們對ES5也要有簡單瞭解,ES5方式

var React = require('react');
var ReactNative = require('react-native');
var {
    Text,
    View,
    StyleSheet
} = ReactNative;

導入之後我們就可以開始使用了,創建組件我們最重要的就是繼承Component,重寫render方法,render方法返回的就是一個組件的集合,它決定了最終顯示的界面效果。

class CustomComponent extends Component {
    render() {
        return <View style={styles.container}>
            <Text
                style={styles.reduce}
            >-</Text>
            <Text
                style={styles.text}
            >5</Text>
            <Text
                style={styles.add}
            >+</Text>
        </View>
    }
}

要達到我們想要的效果,樣式是必不可少的,對於樣式我們可以直接在組件中實現,如 style={{ flexDirection: 'row'}},它和CSS樣式幾乎是通用的含義,不同的是在ReactNative中採用了駝峯命名方式。例如css中flex-direction在RN中就是把下劃線去掉,然後後面字母大寫。這樣寫了之後我們會發現,樣式重用性就小了,最重要的是樣式和組件都糅合在一起,看起來也不美觀,facebook也不推薦我們這樣做,他推薦的是使用StyleSheet統一管理.上面的樣式實現

const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
        marginBottom: 20,
        marginTop: 20,
    },
    reduce: {
        width: 50,
        height: 40,
        borderWidth: 1,
        textAlign: 'center',
        textAlignVertical: 'center',
        borderColor: '#ccc',
        borderBottomLeftRadius: 5,
        borderTopLeftRadius: 5
    },
    text: {
        width: 50,
        height: 40,
        borderTopWidth: 1,
        borderBottomWidth: 1,
        textAlign: 'center',
        textAlignVertical: 'center',
        borderColor: '#ccc',
    },
    add: {
        width: 50,
        height: 40,
        borderWidth: 1,
        textAlign: 'center',
        textAlignVertical: 'center',
        borderColor: '#ccc',
        borderBottomRightRadius: 5,
        borderTopRightRadius: 5,
    }
})

在RN中,組件的默認是縱向排列的,所以我們在View組件樣式中通過flexDirection設置水平(row)排列。然後就是設置邊框寬度,邊框顏色及邊框的圓角半徑,通過textAlign設置文字水平居中,通過textAlignVertical設置文字垂直居中。

組件的使用

使用就很簡單了,和我們使用基本組件套路是一樣的,在使用之前,我們要將定義的組件CustomComponent 暴露出來,共外部使用,在ES6中,導出組件如下

export default class CustomComponent extends Component {}

ES6使用module.exports=CustomComponent 導出,使用就很簡單了,如下

<CustomComponent />

狀態和屬性

上面實現過後,我們就能看到文字開頭圖片實現的效果了,但是我們還沒有實現事件監聽功能,即當點擊左側減號中間的數字減1,直到0,當點擊加號時,中間文本要加1。要實現這種功能就需要了解state(狀態)了。當state發生改變的時候,組件會重新執行render函數刷新界面的數據。
一般的我們會在constructor方法中初始化state.(如果使用es5通過getInitialState函數初始化,es6中無此方法),例如我們在組件初始化時將數量設置爲0

 constructor(props) {
        super(props)
        //ES6寫法,ES5 getInitialState
        this.state = {
            count: 0,
        }
    }

然後我們分別給減號和加號的Text添加點擊(onPress)監聽,並更改state中count的值,使其重新執行render刷新界面數據。render部分代碼更改如下

 render() {
        return <View style={styles.container}>
            <Text
                style={styles.reduce}
                onPress={this.reduce}
            >-</Text>
            <Text
                style={styles.text}
            >{this.state.count}</Text>
            <Text
                style={styles.add}
                onPress={this.add}
            >+</Text>
        </View>
    }

當點擊時,首先通過this.state.count獲取當前的值,如果點擊減號就將該值減1,點擊加號就將該值加1,最終要的是需要通過this.setState()將state更新改變後的值。當state發生改變時,render函數執行,則此時我們看到的數據已經是更改過後的值。

點擊事件

reduce = () => {
        this.setState({
            count: this.state.count > 1 ? this.state.count - 1 : 0
        })
    }
add = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

通過我們分別給加號和減號增加點擊事件onPress,實現了數量加減的功能。

可能很多時候我們要碰到這個問題,例如,當我們從一個商品列表進入商品詳情時往往會有了一個購買數量,通俗的說,我們需要給我定義組件傳遞一個值,這個值就是我們之前已經選擇的商品數量。那麼要實現這樣的效果就需要用到屬性了。
在前面介紹狀態的時候,我們重寫構造函數constructor,他有一個參數props,這就是屬性,在使用組件時所傳入的屬性都可以通過this.props.屬性名 獲得。此時我們對構造函數做一下修改,如下

    constructor(props) {
        super(props)
        //ES6寫法,ES5 getInitialState
        this.state = {
            count: this.props.count,
        }
    }

count的初始值不在是0,而是爲我們定義的值,問題又來了,如果使用組件的時候設置屬性count,那麼這樣取值不是有問題嗎,那如何解決呢。這就用到defaultProps,我們可以使用它定義默認的屬性值,即如果有傳值的時候就用傳的值,如果沒有傳值就用默認值。我們定義默認值爲1

    static  defaultProps = {
        count: 1
    }

這樣發現組件使用起來很不錯的感覺,可是有一天,有人使用組件時傳了一個不是數字的值....字符串。你可能會說,這是使用者的問題,but...我們還是要解決的,在RN中提供了屬性類型檢查功能,約束我們傳入的值。

    static propTypes={
        count:PropTypes.number,
    }

通過上面後,就做了一個類型檢查,限制數字類型,再類型檢查時我們還可以使用isRequired限制該屬性是否必填。屬性功能很強大,不僅能傳值,還有以傳函數。再次就不多介紹。
在使用時給組件加入屬性count

<CustomComponent 
    count=3
  />

生命週期

每個組件都有生命週期方法,我們可以重寫這些生命週期方法在運行時特定的世時間執行。
總體來說生命週期分爲三類

Mounting

該狀態表示某個組件被創建的,也就是初始化組件。

  • constructor(props)
    當然構造方法constructor是必不可少的,它是被裝載之前調用,也是最先調用的函數,當我們重寫constructor時候,必須加參數props,並s首先調用super(props),否則在構造方法中使用props都是undefined,顯然這回導致c出現嚴重的bug.,在這裏我們一般要做的是根據屬性對狀態進行初始化。
  • componentWillMount()
    在組件開始渲染之前調用,也就是再render方法之前,在這個階段並沒有對組件進行渲染
  • render()
    該方法在組件中是必須的,一旦調用,則去檢查 this.props 和 this.state 的數據並返回一個 React 元素。render() 方法不能修改組件的 state,同時需要注意的是,shouldComponentUpdate() 方法必須返回 true,否則當狀態或者屬性值發生改變時將不會再執行 render() 方法。
  • componentDidMount()
    當組件被渲染之後,會被調用,用來通知組件已經加載完成,通常我們會在這裏去從服務器拉取數據來渲染頁面。在這個方法中設置狀態組件將會被重新渲染。

Updating

  • componentWillReceiveProps(nextProps)
    當組件接收到一個新的屬性時,將會被調用,如果我們需要根據屬性對狀態進行更改,(可以通過this.props和nextProps對比)可以在此設置state,在該函數參數nextProps即爲更改後的屬性。
  • shouldComponentUpdate(nextProps, nextState)
    當組件接收到組件 state 或者props發生改變時 ,該方法鍵會被調用,參數nextProps爲設置的屬性,nextState爲設置的狀態。一般我們會根據this.props和nextProps或者this.state和nextState對比,值是否發生變化來返回true或者false.如果返回true就會重新渲染界面,否則的話就不會繼續執行渲染邏輯
  • componentWillUpdate(nextProps, nextState)
    該函數在接收到屬性或者狀態時調用,並且在render之前,shouldComponentUpdate之後調用,shouldComponentUpdate如果返回false的話,該方法就不會調用了。
  • render()
    與Mounting狀態下的render作用一樣。
  • componentDidUpdate(prevProps, prevState)
    表示調用 render() 方法完成了界面的更新,需要注意的是,該方法在初始的 render 後將不會被調用,只有設置屬性或者狀態時纔會調用。參數是更改之前的屬性和狀態,此時通過this.state或者this.props已經是更改後的值。

Unmounting

  • componentWillUnmount ()
    在組件被銷燬或者退出的時候調用,在該方法中我們可以執行任何必要的清理工作,比如失效計時器,取消網絡請求等

生命週期回調順序

在前文我們實現數量加減的組件的基礎上,我們重寫所擁有生命週期並在生命週期中加入console打印函數名來觀察執行順序。

初始化組件:

constructor
componentWillMount
render
componentDidMount

設置狀態時

shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

設置屬性時

componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

卸載組件

componentWillUnmount

到這裏,今天這篇文章要介紹的內容也就介紹完畢了,當然還有很多知識點是沒有提到的,可以去參考ReactNative的官方網站。由於我也是水平有限,難免會有想不到或者說理解不到位的地方,若在閱讀文章的時候發現錯誤的地方,歡迎指正,我及時更改,以免誤導大家。

如果你對學習RN有興趣,可以訪問我的學習記錄的項目,GitHub地址

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