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);

  

三、演示

 

 

posted @ 2020-03-31 16:36  XYQ全哥  阅读(1157)  评论(2编辑  收藏  举报