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