In the code below, I am attempting to provide an onboarding component that enables the user to select out keywords they are interested in. In doing so, a function is triggered that changes the state of that keyword to true, and changes the color of the component to a color that indicates that that keyword is in fact selected.
The problem is that my current code is very redundant in naming these keywords as state (which are also in an array in the same code file). Is there anyway to do this dynamically, in which there is a state array of selected keywords rather than 30 states indicated by each keyword that change when pressed?
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,
},
});
which looks like this:
Determined answer in the following subset of code. Made a state array and updated a temp array that has the same boolean states as the state array to avoid direct manipulation (outside of 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,
},
});