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

  

三、演示

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM