首页 > 解决方案 > 多个 React Native Modal 中的动态值

问题描述

我在车辆比较屏幕中有 6 个下拉菜单。下拉菜单是使用 React Modal 创建的。下拉列表中的所有数据都是动态的。

下拉菜单

加载页面时,要在前两个模态中显示的数据,汽车品牌,被提取并显示在模态下拉列表中。

When a car brand is selected another api call is made to get the models available in that brand. 此数据填充在第二个下拉列表中。

由于选择两个不同品牌的模型有两个下拉列表,因此选择一个品牌时,两个模型下拉列表都将更新。

如果我必须添加更多数量的汽车进行比较,如何更改我的代码以便我可以使其工作并使其在未来可扩展?

比较.js

import React, { Component } from 'react'
import {
    View,
    StyleSheet,
    ScrollView,
    SafeAreaView,
    ActivityIndicator,
} from 'react-native';


import PickerModal from '../components/PickerModal';
import * as Api from "../api/app";


export default class CompareVehicles extends Component {
    constructor (props) {
        super(props);
        this.state = {
            isLoading: true,
            vehicleCompany: [],
            vehicleModel: [],
            vehicleSubModel: [],
        };
    }

    componentDidMount() {
        this.setState({
            isLoading: true,
        });
        this.getVehicleBrand();
    }

    getVehicleBrand = () => {
        Api.getVehicleBrands()
            .then((responseJson) => {
                console.log(responseJson);
                if (responseJson.success === true){
                    this.setState({
                        isLoading: false,
                        vehicleCompany : responseJson.data
                    });
                }  else {
                    alert("Error Loading Content")
                }
            });
    };

    submitBrand = async (data) => {
        Api.getVehicleModel(data)
            .then((responseJson) => {
                console.log(responseJson);
                if (responseJson.success === true){
                    this.setState({
                        vehicleModel: responseJson.data
                    });
                }  else {
                    alert("Error Adding Content")
                }
            });
    };

    submitModel = (data) => {
        console.log(data)
    };

    submitVariant = (data) => {
        console.log(data)
    };

    _renderPickerModal = (index) => {
        if (this.state.vehicleSubModel.length) {
            return (
                <View>
                    <PickerModal onSubmit={this.submitBrand} type={'light-dropdown'} data={this.state.vehicleCompany}/>
                    <PickerModal onSubmit={this.submitModel} type={'light-dropdown'} data={this.state.vehicleModel}/>
                    <PickerModal onSubmit={this.submitVariant} type={'light-dropdown'} data={this.state.vehicleSubModel}/>
                </View>
            )
        } else if(this.state.vehicleModel.length) {
            return (
                <View>
                    <PickerModal onSubmit={this.submitBrand} type={'light-dropdown'} data={this.state.vehicleCompany}/>
                    <PickerModal onSubmit={this.submitModel} type={'light-dropdown'} data={this.state.vehicleModel}/>
                </View>
            )
        } else if (this.state.vehicleCompany.length) {
            return (
                <View>
                    <PickerModal onSubmit={this.submitBrand} type={'light-dropdown'} data={this.state.vehicleCompany}/>
                </View>
            )
        }
    };

    render() {
        if(this.state.isLoading) {
            return (
                <SafeAreaView style={[styles.safeArea, styles.alignJustifyCenter]}>
                    <ActivityIndicator/>
                </SafeAreaView>
            );
        } else {
            return (
                <SafeAreaView style={styles.safeArea}>
                    <ScrollView
                        style={styles.scrollView}
                        scrollEventThrottle={200}
                        directionalLockEnabled={true}>
                        <View style={{flexDirection: 'row'}}>
                            <View style={{flex: 1}}>
                                {this._renderPickerModal}
                            </View>
                            <View style={{flex: 1}}>
                                {this._renderPickerModal}
                            </View>
                        </View>
                    </ScrollView>
                </SafeAreaView>
            );
        }
    }
}


const styles = StyleSheet.create({
    safeArea: {
        flex: 1,
        backgroundColor: '#ffffff',
    },
    alignJustifyCenter: {
        alignItems: 'center',
        justifyContent: 'center'
    },
    scrollView: {
        flex: 1,
        backgroundColor: '#fff',
        paddingVertical: 15,
        paddingHorizontal: 20
    }
});

PickerModal.js

import React, {Component} from 'react';
import { StyleSheet, Text, View, Modal, TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback } from 'react-native';
import PropTypes from 'prop-types';
import Ionicons from 'react-native-vector-icons/Ionicons';

export default class PickerModal extends Component {
    static propTypes = {
        type: PropTypes.string.isRequired,
        data: PropTypes.array.isRequired,
        onSubmit: PropTypes.func.isRequired,
        index: PropTypes.number
    };

    constructor(props) {
        super(props);

        this.state = {
            pickerTitle: this.props.data[0].name,
            pickerValue: this.props.data[0].id,
            pickerDisplayed: false,
            index: this.props.index
        }
    }

    componentDidMount = () => {
        console.log(this.props)
    };


    submit = () => {
        const { pickerValue } = this.state;
        const { index } = this.state;
        if (pickerValue) {
            this.props.onSubmit(pickerValue, index);
        }
    };

    setPickerValue(content, index) {
        this.setState({
            pickerTitle: content.name,
            pickerValue: content.id,
            index: index
        }, () => this.submit());

        this.togglePicker();
    }

    togglePicker() {
        this.setState({
            pickerDisplayed: !this.state.pickerDisplayed
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <TouchableHighlight
                    style={{width: '90%'}}
                    onPress={() => this.togglePicker()}
                    underlayColor='transparent'>
                    <View style={[styles.dropdown, this.props.type == 'dark-dropdown' ? styles.darkDropdown : styles.lightDropdown]}>
                        <Text style={[this.props.type == 'dark-dropdown' ? styles.darkDropdown : {}, {flex: 1}]}>{this.state.pickerTitle}</Text>
                        <Ionicons name={'md-arrow-dropdown'} size={25} style={[this.props.type == 'dark-dropdown' ? styles.colorWhite : {}, {marginLeft: 5, marginTop: 5}]}/>
                    </View>
                </TouchableHighlight>
                <Modal visible={this.state.pickerDisplayed} animationType={"fade"} transparent={true}>
                    <TouchableOpacity
                        activeOpacity={1}
                        style={{flex:1, justifyContent:'center', alignItems:'center', backgroundColor: 'rgba(0, 0, 0, 0.3)'}}
                        onPressOut={() => {this.togglePicker()}}>
                        <TouchableWithoutFeedback>
                            <View style={{padding: 20,
                                backgroundColor: '#ffffff',
                                bottom: 0,
                                left: 0,
                                right: 0,
                                alignItems: 'center',
                                position: 'absolute', width: '100%' }}>
                                { this.props.data.map((value, index) => {
                                    return <TouchableHighlight key={index} onPress={() => this.setPickerValue(value, this.props.index)} style={{ paddingTop: 4, paddingBottom: 4 }}>
                                        <Text style={{fontSize: 15}}>{ value.name }</Text>
                                    </TouchableHighlight>
                                })}

                                <TouchableHighlight onPress={() => this.togglePicker()} style={{ paddingTop: 50, paddingBottom: 20 }}>
                                    <Text style={{color: '#999', fontSize: 20}}>Cancel</Text>
                                </TouchableHighlight>
                            </View>
                        </TouchableWithoutFeedback>
                    </TouchableOpacity>
                </Modal>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    },
    colorWhite: {
        color: '#fff'
    },
    darkDropdown: {
        backgroundColor: '#000',
        borderRadius: 8,
        color: '#fff'
    },
    lightDropdown: {
        borderBottomWidth: 0.3,
        borderBottomColor: '#000'
    },
    dropdown: {
        flexDirection: 'row',
        paddingHorizontal: 5,
        paddingVertical: 0,
        alignItems: 'center',
        justifyContent: 'center',
    }
});

标签: reactjsreact-nativereact-modal

解决方案


我相信你所做的已经足够接近了。我将创建一个中间容器组件,它应该接收品牌并与需要比较的汽车数量完全隔离。

您只需执行以下操作:

render(){
     this.state.brandsToCompare.map(brand => <PickerModalContainer brand={brand}/>)
}

因此,主组件只负责管理用户是否选择了 1、2 或他们想要比较的任何数量的品牌或汽车。PickerModalContainer 将具有获取逻辑,并且根据用户选择的内容,您只需获取并更新其他 PickerModal。这样你就真的不关心其他的 Picker,因为他们不“认识”彼此。

如果最后您需要获取一些信息进行比较,您可以将函数作为道具公开给 PickerModalContainer 可以与下面的正确 PickerModal 对话并从该模态返回您需要的任何内容。

老实说,我认为这并没有太大的变化,只是一些重构。


推荐阅读