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地址