一、简介
前面都是介绍关于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);
三、演示
来源:oschina
链接:https://my.oschina.net/u/4364002/blog/4308253