消除用户入职关键字选择中的极端冗余

Eliminating extreme redundancies in User Onboarding Keyword Selection

在下面的代码中,我试图提供一个入门组件,使用户能够 select 输出他们感兴趣的关键字。这样做时,会触发一个函数来更改该关键字的状态为 true,并将组件的颜色更改为表明该关键字实际上已 selected 的颜色。

问题是我当前的代码将这些关键字命名为状态(它们也在同一代码文件中的数组中)是非常多余的。无论如何动态地执行此操作,其中有一个 selected 关键字的状态数组,而不是每个关键字指示的 30 个状态,这些状态在按下时会发生变化?

var React = require('react-native');
var {
    View, 
    ScrollView, 
    Image,
    StyleSheet,
    Text, 
    TouchableHighlight,
} = React;

//additional libraries
var Parse = require('parse/react-native'); //parse for data storage
Icon = require('react-native-vector-icons/Ionicons'); //vector icons
var LinearGradient = require('react-native-linear-gradient'); //linear grad. over button

//dimensions
var Dimensions = require('Dimensions');
var window = Dimensions.get('window');

//dynamic variable components
var ImageButton = require('../common/imageButton');
var KeywordBox = require('./onboarding/keyword-box');
var ActionButton = require('../common/ActionButton');

module.exports = React.createClass({
    getInitialState: function() {
        return {
            isFinished: false,
            BlackLivesMatter: false,
            Adventure_Travel: false,
            Arts: false,
            Auto: false,
            Basketball: false,
            Book_Reviews: false,
            Business: false,
            Celebrity_News: false,
            Computers: false,
            Design: false,
            Entertainment: false,
            Entrepreneurship: false,
            Fashion_Trends: false,
            Film: false,
            Happiness: false,
            Health: false,
            Hip_Hop: false,
            History: false,
            Humor: false,
            Internet: false,
            LGBQT: false,
            Management: false,
            Marketing: false,
            Mobile_Games: false,
            Music: false,
            News: false,
            Performing_Arts: false,
            Photography: false,
            Politics: false,
            Sports: false,
            Technology: false,
            Travel: false,
            Trending: false,
            Universe: false,
            Women_of_Color: false,
            World: false,
        };
    }, 
    render: function() {
        return (
            <View style={[styles.container]}>
                <Image 
                    style={styles.bg} 
                    source={require('./img/login_bg1_3x.png')}>
                    <View style={[styles.header, this.border('red')]}>
                        <View style={[styles.headerWrapper]} >
                            <Image 
                                resizeMode={'contain'}
                                style={[styles.onboardMsg]}
                                source={require('./img/onboard_msg.png')} >
                            </Image>
                        </View>
                    </View>
                    <View style={[styles.footer, this.border('blue')]}>
                        <ScrollView 
                            showsVerticalScrollIndicator={false}
                            showsHorizontalScrollIndicator={false}
                            horizontal={false}
                            style={styles.footerWrapperNC}
                            contentContainerStyle={[styles.footerWrapper]}>
                            {this.renderKeywordBoxes()}
                        </ScrollView>
                    </View>
                </Image>
                <ActionButton 
                    onPress={this.onNextPress}
                    buttonColor="rgba(0,0,0,0.7)" />
            </View>
        );
    }, 
    renderKeywordBoxes: function() {
        //renders array of keywords in keyword.js
        //and maps them onto custom component keywordbox to show in the onboarding
        //component
        var Keywords = ['LGBQT', 'BlackLivesMatter', 'Arts', 'Hip-Hop', 'History', 
        'Politics', 'Fashion Trends', 'Entrepreneurship', 'Technology', 'Business', 
        'World', 'Health', 'Trending', 'Music', 'Sports', 'Entertianment', 
        'Mobile Games', 'Design', 'News', 'Humor', 'Happiness', 'Women of Color', 'Travel',
        'Photography','Computers', 'Universe', 'Internet','Performing Arts','Management',
         'Celebrity News', 'Book Reviews', 'Marketing', 'Basketball', 'Film', 'Adventure Travel', 
         'Auto'];

        return Keywords.map(function(keyword, i) {
            return <KeywordBox 
                key={i} text={keyword} 
                onPress={ () => { this.onKeywordPress(keyword) }}
                selected={this.state.+keyword}/>
        });
    }, 
    onKeywordPress: function(keyword) {

        //take the spaces out the word
        keyword = keyword.split(' ').join('_');
        keyword = keyword.split('-').join('_');


        //change the state accordingly without doing so directly 
        let newState = {...this.state};
        newState[keyword] = !newState[keyword];
        this.setState(newState);
        console.log(keyword, newState);
    }, 
    onNextPress: function() {

    }, 
    //function that helps with laying out flexbox itmes 
    //takes a color argument to construct border, this is an additional 
    //style because we dont want to mess up our real styling 
     border: function(color) {
        return {
          //borderColor: color, 
          //borderWidth: 4,
        } 
     },
});

styles = StyleSheet.create({
    header: {
        flex: 2,
    }, 
    headerWrapper: {
        flex: 1, 
        flexDirection: 'column', 
        alignItems: 'center',
        justifyContent:'space-around',
        marginTop: window.height/35,
    },
    onboardMsg: {
        width: (window.width/1.2), 
        height: (452/1287)*((window.width/1.2)),
    },
    footer: {
        flex: 7, 
        marginTop: window.height/35,
        marginLeft: window.width/30,
    }, 
    //container style wrapper for scrollview
    footerWrapper: {
        flexWrap: 'wrap', 
        alignItems: 'flex-start',
        flexDirection:'row',
    },
    //non-container style wrapper for scrollview
    footerWrapperNC: {
        flexDirection:'column',
    },
    container: {
        flex: 1, 
        alignItems: 'center', 
        justifyContent: 'center',
    }, 
    bg: {
        flex: 1,
        width: window.width, 
        height: window.height, 
    },
});

看起来像这样:

以下代码子集中的确定答案。创建一个状态数组并更新一个与状态数组具有相同布尔状态的临时数组,以避免直接操作(在 setState 之外):

var React = require('react-native');
var {
    View, 
    ScrollView, 
    Image,
    StyleSheet,
    Text, 
    TouchableHighlight,
} = React;

//additional libraries
var Parse = require('parse/react-native'); //parse for data storage
Icon = require('react-native-vector-icons/Ionicons'); //vector icons
var LinearGradient = require('react-native-linear-gradient'); //linear grad. over button

//need react-addons-update to use immutability helpers*******

//dimensions
var Dimensions = require('Dimensions');
var window = Dimensions.get('window');

//dynamic variable components
var ImageButton = require('../common/imageButton');
var KeywordBox = require('./onboarding/keyword-box');
var ActionButton = require('../common/ActionButton');

module.exports = React.createClass({ 
    getInitialState: function() {
        return {
            keywords_array: Array.apply(null, Array(37)).map(Boolean.prototype.valueOf,false)
        };
    }, 
    render: function() {
        var newData = this.state.keywords_array;
        return (
            <View style={[styles.container]}>
                <Image 
                    style={styles.bg} 
                    source={require('./img/login_bg1_3x.png')}>
                    <View style={[styles.header, this.border('red')]}>
                        <View style={[styles.headerWrapper]} >
                            <Image 
                                resizeMode={'contain'}
                                style={[styles.onboardMsg]}
                                source={require('./img/onboard_msg.png')} >
                            </Image>
                        </View>
                    </View>
                    <View style={[styles.footer, this.border('blue')]}>
                        <ScrollView 
                            showsVerticalScrollIndicator={false}
                            showsHorizontalScrollIndicator={false}
                            horizontal={false}
                            style={styles.footerWrapperNC}
                            contentContainerStyle={[styles.footerWrapper]}>
                            {this.renderKeywordBoxes(newData)}
                        </ScrollView>
                    </View>
                </Image>
                <ActionButton 
                    onPress={this.onNextPress}
                    buttonColor="rgba(0,0,0,0.7)" />
            </View>
        );
    }, 
    renderKeywordBoxes: function(newData) {
        //renders array of keywords in keyword.js
        //and maps them onto custom component keywordbox to show in the onboarding
        //component
        var Keywords = ['LGBQT', 'BlackLivesMatter', 'Arts', 'Hip-Hop', 'History', 
        'Politics', 'Fashion Trends', 'Entrepreneurship', 'Technology', 'Business', 
        'World', 'Health', 'Trending', 'Music', 'Sports', 'Entertianment', 
        'Mobile Games', 'Design', 'News', 'Humor', 'Happiness', 'Women of Color', 'Travel',
        'Photography','Computers', 'Universe', 'Internet','Performing Arts','Management',
         'Celebrity News', 'Book Reviews', 'Marketing', 'Basketball', 'Film', 'Adventure Travel', 
         'Auto'];

         var that = this;

        return Keywords.map(function(keyword, i) {

            return <KeywordBox 
                key={i} 
                text={keyword} 
                onPress={ () => { that.onKeywordPress(i, keyword, newData) }}
                selected={newData[i]} />
        });
    }, 

    onKeywordPress: function(i, keyword, newData) {
        console.log(this.state.keywords_array);
        console.log(keyword); 
        console.log(newData); 

        //change the state accordingly without doing so directly 
        var array = newData;
        array[i] = true; 
        this.setState({
          keywords_array: array
        });

        console.log(array);
        console.log(this.state.keywords_array);

    }, 
    onNextPress: function() {

    }, 
    //function that helps with laying out flexbox itmes 
    //takes a color argument to construct border, this is an additional 
    //style because we dont want to mess up our real styling 
     border: function(color) {
        return {
          //borderColor: color, 
          //borderWidth: 4,
        } 
     },
});

styles = StyleSheet.create({
    header: {
        flex: 2,
    }, 
    headerWrapper: {
        flex: 1, 
        flexDirection: 'column', 
        alignItems: 'center',
        justifyContent:'space-around',
        marginTop: window.height/35,
    },
    onboardMsg: {
        width: (window.width/1.2), 
        height: (452/1287)*((window.width/1.2)),
    },
    footer: {
        flex: 7, 
        marginTop: window.height/35,
        marginLeft: window.width/30,
    }, 
    //container style wrapper for scrollview
    footerWrapper: {
        flexWrap: 'wrap', 
        alignItems: 'flex-start',
        flexDirection:'row',
    },
    //non-container style wrapper for scrollview
    footerWrapperNC: {
        flexDirection:'column',
    },
    container: {
        flex: 1, 
        alignItems: 'center', 
        justifyContent: 'center',
    }, 
    bg: {
        flex: 1,
        width: window.width, 
        height: window.height, 
    },
});

创建一个状态数组和一个临时数组(已更新),它们与状态数组具有相同的布尔状态,以避免直接操作(在 setState 之外)。