ReactNative实现自定义组件
【摘要】 一、前言ReactNative组件分为类组件和函数组件。实现ReactNative自定义组件基本流程如下:组件需要实现什么效果;组件基本逻辑是怎样的;继承Component或PureComponent实现render()方法;定义组件属性字段及其类型,以及是否必须,如不必须则默认字段值是什么;绑定点击或者触摸等事件方法,执行前需要判空;注册、调用。 二、参数传递React Native版本...
一、前言
ReactNative
组件分为类组件和函数组件。
实现ReactNative
自定义组件基本流程如下:
- 组件需要实现什么效果;
- 组件基本逻辑是怎样的;
- 继承
Component
或PureComponent
实现render()
方法;- 定义组件属性字段及其类型,以及是否必须,如不必须则默认字段值是什么;
- 绑定点击或者触摸等事件方法,执行前需要判空;
- 注册、调用。
二、参数传递
React Native
版本升级很快,已经升级到0.60.3
,在新版本环境下运行老项目会有一些问题,常见的就是参数属性校验错误的问题。例如:
造成上面错误的原因是随着React Native
的升级,新版本废弃了很多东西,过去我们可以直接使用 React.PropTypes
来进行属性校验,不过这个属性自 React v15.5
起就被移除了,转而使用prop-types
库来进行替换。
子组件接收父组件传参过程中,可以通过propTypes
校验参数类型。
PropTypes
提供了许多验证工具,用来帮助开发者确定props
数据的有效性。在下面的示例中,我们使用了PropTypes.stirng
。用以说明name的值类型应该是string
。 当Component
的props
接收到一个无效值时,浏览器控制台就会输出一个警告, 控制台会出现如下警告:
static propTypes = {
Greeting: PropTypes.string,
gravity: PropTypes.string,
}
处于性能原因考虑,类型检查仅在开发模式下进行。
从软件工程角度考量,可将校验规则封装一起,定义一套校验工具。示例如下:
import React from 'react';
import PropTypes from 'prop-types';
class PropValidatorComponent extends React.Component {
render() {
}
}
PropValidatorComponent.propTypes = {
//可以定义一个属性是特定的JS类型(Array,Boolean,Function,Number,Object,String,Symbol)。默认情况下,这些都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
//指定类型为:任何可以被渲染的元素,包括数字,字符串,react 元素,数组,fragment。
optionalNode: PropTypes.node,
// 指定类型为:一个react元素
optionalElement: PropTypes.element,
//可以定义类型为某个类的实例,这里使用JS的instanceOf操作符实现
optionalMessage: PropTypes.instanceOf(Message),
//指定枚举类型:你可以把属性限制在某些特定值之内
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 指定多个类型:也可以把属性类型限制在某些指定的类型范围内
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 指定某个类型的数组
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 指定类型为对象,且对象属性值是特定的类型
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
//指定类型为对象,且可以规定哪些属性必须有,哪些属性可以没有
optionalObjectWithShape: PropTypes.shape({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),
// 指定类型为对象,且可以指定对象的哪些属性必须有,哪些属性可以没有。如果出现没有定义的属性,会出现警告。
//下面的代码optionalObjectWithStrictShape的属性值为对象,但是对象的属性最多有两个,optionalProperty 和 requiredProperty。
//出现第三个属性,控制台出现警告。
optionalObjectWithStrictShape: PropTypes.exact({
optionalProperty: PropTypes.string,
requiredProperty: PropTypes.number.isRequired
}),
//加上isReqired限制,可以指定某个属性必须提供,如果没有出现警告。
requiredFunc: PropTypes.func.isRequired,
requiredAny: PropTypes.any.isRequired,
// 也可以指定一个自定义的验证器。如果验证不通过,它应该返回Error对象,而不是`console.warn `或抛出错误。`oneOfType`中不起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
//也可以提供一个自定义的验证器 arrayOf和objectOf。如果验证失败,它应该返回一个Error对象。
//验证器用来验证数组或对象的每个值。验证器的前两个参数是数组或对象本身,还有对应的key。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
使用时,父组件通过属性名作为子组件参数形式传递,子组件通过props
属性接收参数值,并可以通过设置默认值。
// ES6
class Greeting extends React.Component {
static defaultProps = {
name: 'stranger'
}
render() {
return (
<div>Hello, {this.props.name}</div>
)
}
}
三、示例
3.2 LinearLayout 组件
import React, { PureComponent } from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
export default class LinearLayout extends PureComponent {
static propTypes = {
orientation: PropTypes.string,
gravity: PropTypes.string,
}
static defaultProps = {
orientation: 'vertical',
gravity: undefined,
}
render() {
return(
<View style={{
flexDirection: (this.props.orientation === 'horizontal') ? 'row' : 'column'
}}>
{this.props.children}
</View>
)
}
}
组件引用:
import LinearLayout from './LinearLayout';
<LinearLayout orientation={this.state.orientation}>
<Text>ChildComponent</Text>
</LinearLayout>
3.2 列表展示
下面以封装FlatList
展示列表信息,实现自定义组件。
/** ListView 使用说明
* extraData:数据变动会不会重新渲染列表
*
* data:为数据源
* onLoading:上拉回调
* isLoading:上拉加载中
* hasMore:是否有更多数据
* hint:提醒用户文字
* isRefresh:下拉刷新中
* onRefresh:下拉刷新回调
* renderItem:item界面布局
*/
import React from 'react';
import { StyleSheet, FlatList, View, Text, Image, ActivityIndicator, RefreshControl, Dimensions } from 'react-native';
import { Images } from '../../../constant/Images';
const { width, height } = Dimensions.get('window'); //获取手机的宽和高
export default (props) => (
<FlatList
testID={props.testID}
data={props.data}
style={props.style || { flex: 1 }}
refreshControl={
<RefreshControl
onRefresh={props.isLoading ? null : props.onRefresh}
refreshing={!props.isLoading && props.isRefresh}
colors={props.colors || ['#108EE9', '#00FF00', '#FF0000']}
/>
}
numColumns={props.numColumns || 1}
extraData={props.extraData}
renderItem={props.renderItem}
keyExtractor={(_, index) => `${index}`}
indicatorStyle={props.indicatorStyle}
ListEmptyComponent={EmptyView(props)}
ListFooterComponent={FooterView(props)}
ListHeaderComponent={props.ListHeaderComponent && props.ListHeaderComponent()}
showsVerticalScrollIndicator={props.showIndicator}
onEndReachedThreshold={props.threshold || 0.1}
onScroll={props.onScroll}
ItemSeparatorComponent={props.ItemSeparatorComponent}
onEndReached={() => (props.isLoading || props.isRefresh || !props.hasMore ? null : props.onLoading())}
/>
);
// 数据为空是的视图
const EmptyView = (props) =>
props.isLoading || props.isRefresh ? null : (
<View style={styles.emptyLayout}>
<Image style={styles.icon} source={props.icon || showIcon(props.hint)} />
<Text style={styles.hint} onPress={hintLoad(props.hint) ? props.hintCallback || props.onLoading : null}>
{props.hint || '没有查询到数据'}
</Text>
</View>
);
// 列表底部视图
const FooterView = (props) =>
props.isRefresh || (!props.isLoading && props.data.length == 0) ? null : (
<View style={styles.footerLayout}>
{props.isLoading ? <ActivityIndicator style={styles.indicator} color="#aaa" size="small" /> : null}
<Text style={styles.loadText} onPress={hintLoad(props.hint) ? props.hintCallback : null}>
{props.hint || (props.hasMore || props.isLoading ? '加载中...' : '没有更多数据!')}
</Text>
</View>
);
// 判断显示图片
function showIcon(msg) {
if (msg == null || msg.indexOf('没有查询到数据') > 0 || msg.indexOf('数据为空') > 0) {
return Images.nomes;
} else if (msg.indexOf('超时') > 0) {
return Images.nomes;
} else if (msg.indexOf('异常') > 0) {
return Images.nomes;
}
return Images.nomes;
}
// 判断点击重新加载的条件
function hintLoad(msg) {
return msg == null || msg.indexOf('异常') > 0 || msg.indexOf('失败') > 0 || msg.indexOf('超时') > 0;
}
const styles = StyleSheet.create({
emptyLayout: {
// marginTop: 96,
marginTop: Math.floor(height / 4),
alignItems: 'center',
justifyContent: 'center',
},
icon: {
width: 128,
height: 128,
resizeMode: 'contain',
},
hint: {
color: '#666666',
fontSize: 14,
},
footerLayout: {
minHeight: 38,
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'center',
marginBottom: 20,
},
indicator: {
marginRight: 8,
},
loadText: {
color: '#999',
fontSize: 14,
},
});
组件引用:
import { ListView } from './ListView.js';
<ListView
data={bloc.getCity()}
numColumns={2}
hasMore={false}
isLoading={true}
onRefresh={_onRefreshEvent}
// onLoading={_onLoadEvent}
renderItem={sectionItems}
isLoading={false}
style={{marginTop: 10}}
/>
四、拓展阅读
- 《ReactNative进阶(十六):组件生命周期》
- 《ReactNative进阶(二十六):父子组件函数调用》
- 《ReactNative进阶(三十):Component、PureComponent 解析》
- 《ReactNative进阶(四十六):页面跳转及传参》
- 《Vue进阶(九十四):自定义组件》
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)