一、簡介
前面都是介紹關於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);
三、演示

