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