ReactNative元件詳解

Code4Android發表於2017-08-01

ReactNative學習記錄傳送門

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

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

自定義元件

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

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

image.png
image.png

要實現這樣的一個元件,我們首先要知道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地址

相關文章