ReactNative: 组件封装(如二级菜单组件)
一、简介
前面都是介绍关于RN基本的API组件和UI组件,这些组件在复杂的复合组件中都是以颗粒度的形式存在的,如何有效合理的利用它们进行封装,是十分有必要的。开发复合组件的好处有很多,最为明显的就是复用和独立功能模块。复合组件分为两种,一种是静态的,这种组件不具备重用的特征,由静态数据组成,开发静态页面即可,不考虑数据的传递。另一种就是动态组件,它可以通过接收外部传入的动态数据进行联动,达到组件复用的效果。
二、应用
动态组件有很多应用,最典型的例如二级菜单组件,样式固定,数据可变,通过一级目录的选择来联动刷新二级目录的数据。思路很简单,首先构建数据模型;其次,对要封装的组件进行粒度拆分,依次按照此粒度构建组件;接着,设定组件的属性接口;然后,设计组件渲染规则并分解渲染, 绑定事件;最后,引用封装的组件并传入数据模型即可。完整示例如下:
HeadList.js【粒度组件:头部标签】
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity
} from 'react-native';
export default class HeadList extends Component{
render(){
let headData = this.props.data;
let update = this.props.update;
const count = headData.length;
return (
<View style={style.flex}>
{
headData.map( function(item,i){
return (
<View style={[style.center,{flex:1/count}]} key={i}>
<TouchableOpacity onPress={ () => {update(headData[i])}}>
<Text style={style.head_text}>
{item}
</Text>
</TouchableOpacity>
</View>
)
})
}
</View>
)
}
}
const style = StyleSheet.create({
flex: {
flex: 1,
flexDirection: 'row'
},
head_text: {
color: '#7B7B7B',
fontSize: 20
},
center: {
justifyContent: "center",
alignItems: "center"
}
});
LeftList.js【粒度组件:一级目录】
import React, { Component } from 'react';
import {
StyleSheet,
View,
ScrollView,
TouchableOpacity,
Text,
Dimensions
} from 'react-native';
const {width} = Dimensions.get('window');
export default class LeftList extends Component{
constructor(props){
super(props);
this.state = {
selectIndex:0
};
}
updateState(index,update,leftData){
//触发回调函数
update(leftData[index]);
//重新渲染cell颜色
this.setState({
selectIndex:index
})
};
componentWillReceiveProps(nextProps): void {
if (nextProps.shouldChangeTab) {
//重新渲染cell颜色
this.setState({
selectIndex:0
})
}
}
render(){
let leftData = this.props.data;
let update = this.props.update;
let {selectIndex} = this.state;
return (
<ScrollView style={style.container}>
{
leftData.map( (item,i) => {
return (
<View key={i} style={[style.list_cell,style.center, selectIndex === i ?
style.selectBgColor : style.normalBgColor]}>
<TouchableOpacity onPress = { this.updateState.bind(this,i,update,leftData) } >
<Text style={[style.list_text,style.list_margin]}>
{item}
</Text>
</TouchableOpacity>
</View>
)
})
}
</ScrollView>
)
}
}
const style = StyleSheet.create({
container: {
flex:1,
width: width/2,
backgroundColor:'#F2F2F2'
},
list_text: {
color: '#7B7B7B',
fontSize: 18
},
list_margin: {
marginLeft: 20
},
list_cell: {
height: 60
},
center: {
justifyContent: "center"
},
selectBgColor: {
backgroundColor:'#FFFFFF'
},
normalBgColor: {
backgroundColor:'#F2F2F2'
}
});
RightList.js【粒度组件:二级目录】
import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
Dimensions
} from 'react-native';
const {width} = Dimensions.get('window');
export default class RightList extends Component{
render(){
let rightData = this.props.data;
return (
<ScrollView style={style.container}>
{
rightData.map( function(item,i){
return (
<View key={i} style={[style.list_cell,style.center]}>
<TouchableOpacity>
<Text style={[style.list_text,style.list_margin]}>
{item}
</Text>
</TouchableOpacity>
</View>
)
})
}
</ScrollView>
)
}
}
const style = StyleSheet.create({
container: {
flex:1,
width: width/2,
backgroundColor:'#FFFFFF'
},
list_text: {
color: '#7B7B7B',
fontSize: 18
},
list_margin: {
marginLeft: 20
},
list_cell: {
height: 60
},
center: {
justifyContent: "center"
}
});
MenuList.js【封装的复合组件】
import React, { Component } from 'react';
import {
StyleSheet,
View,
Dimensions
} from 'react-native';
import HeadList from './HeadList'
import LeftList from "./LeftList";
import RightList from "./RightList";
const {height} = Dimensions.get('window');
let data = {};
let headData = [];
let leftData = [];
let rightData = [];
export default class MenuList extends Component{
constructor(props){
super(props);
data = props.data;
//初始化头部数据
for (let item in data){
headData.push(item);
}
//初始化左侧数据
let defaultLValue = headData[0];
for (let item in data[defaultLValue]){
leftData.push(item);
}
//初始化右侧数据
let defaultRValue = leftData[0];
rightData = data[defaultLValue][defaultRValue];
//初始化state
this.state = {
shouldChangeTab: false,
currentTab: defaultLValue,
leftData : leftData,
rightData : rightData
};
}
//函数回调,每次选择头部tab后,重新render
forceUpdateAllUI = (ele) => {
leftData = [];
for (let item in data[ele]){
leftData.push(item);
}
let defaultRValue = leftData[0];
rightData = data[ele][defaultRValue];
this.setState({
shouldChangeTab:true,
currentTab: ele,
leftData: leftData,
rightData: rightData
})
};
//函数回调,每次选择左侧列表后,重新render
forceUpdateRightListUI = (ele) => {
rightData = data[this.state.currentTab][ele];
this.setState({
shouldChangeTab:false,
rightData: rightData
})
};
render(){
return (
<View style={style.container}>
<View style={style.top}>
<HeadList data={headData} update={this.forceUpdateAllUI}/>
</View>
<View style={style.bottom}>
<LeftList data={this.state.leftData}
shouldChangeTab={this.state.shouldChangeTab}
update={this.forceUpdateRightListUI}
/>
<RightList data={this.state.rightData}/>
</View>
</View>
)
}
}
const style = StyleSheet.create({
container: {
flex: 1,
height: height,
borderTopWidth: 1,
borderBottomWidth: 1,
borderColor: '#ddd'
},
top: {
height: 60,
borderBottomWidth: 1,
borderColor:'#DFDFDF',
backgroundColor:'#F5F5F5'
},
bottom: {
height: height-60,
flexDirection:'row',
backgroundColor: '#F5FCFF'
}
});
Index.ios.js【引用复合组件】
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, View, StatusBar } from 'react-native'; import MenuList from "./src/MenuList"; const data1 = { "全部区域": { "全部区域": ["全部区域"], "热门商圈": [ "虹桥地区", "徐家汇地区", "淮海路商业区", "静安寺地区", "上海火车站地区", "浦东陆家嘴金融贸易区", "四川北路商业区", "人民广场地区", "南翔,安亭汽车城" ], "热门行政区": [ "静安区", "徐汇区", "长宁区", "黄浦区", "虹口区", "宝山区", "闸北区" ] }, "地铁沿线":{ "地铁全线":["地铁全线"], "一号线":["莘庒站","外环路站","莲花路站","锦江乐园站","上海南站","漕宝路站"], "二号线":["浦东国际机场站","海天三路站","远东大道站","凌空路站"] } }; const data2 = { "Language":{ "All":["All"], "Web Front End":["HTML","CSS","JavaScript"], "Server":["Node.js","Java","Python","Ruby","Php"] }, "Tool":{ "All":["All"], "Apple":["Xcode"], "Other":["Sublime Text","WebStorm","Visual Studio Code"] } }; StatusBar.setHidden(true); export default class RNComponentPackage extends Component { render() { return ( <View style={styles.container}> <MenuList data={data1}/> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFFFFF', } }); AppRegistry.registerComponent('RNComponentPackage', () => RNComponentPackage);
三、演示

程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!
浙公网安备 33010602011771号