Reputation: 65
I have an array of objects that I am currently mapping over to generate as buttons. When clicked, I want the background color of the specific button the user clicks on to change color ( I want it to toggle on, like a switch, so I can eventually save to async storage). Right now when the user clicks, all buttons change color. I'm not quite sure how I should handle this.setState in the selectMetric function.
import React, {Component} from 'react';
import {View, Text, ScrollView} from 'react-native';
import {Button} from 'react-native-elements';
const RISK_DATA = [
{id: 1, text: 'cats', flag: false, buttonColor: null},
{id: 2, text: 'dogs', flag: false, buttonColor: null},
]
class IssueSelectionScreen extends Component {
state = {flag: false, buttonColor: null}
selectMetric = (index) => {
for (let i=0; i < RISK_DATA.length; i++) {
if (index === (RISK_DATA[i].id - 1)) {
console.log("RISK_DATA:", RISK_DATA[i]); // logs matching id
// ------------------------------------------------------
// Problem setting correct state here:
RISK_DATA[i].buttonColor = this.setState({flag: true, buttonColor: '#03A9F4'})
// this.setState({flag: true, buttonColor: '#03A9F4'})
// this.setState({update(this.state.buttonColor[i], {buttonColor: {$set: '#03A9F4'}}) })
// ----------------------------------------------------------
}
}
}
showMetric() {
return RISK_DATA.map((metric, index) => {
return (
<View key={metric.id}>
<Button
raised
color={'black'}
title={metric.text}
borderRadius={12}
onPress={() => this.selectMetric(index)}
backgroundColor={this.state.buttonColor}
>
{metric.text}
</Button>
<Text>{/* intentionally blank*/} </Text>
</View>
)
})
}
render() {
return(
<ScrollView style={styles.wrapper}>
<View style={styles.issues}>
{this.showMetric()}
</View>
</ScrollView>
);
}
}
const styles = {
issues: {
justifyContent: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
marginTop: 10,
justifyContent: 'space-between',
},
wrapper: {
backgroundColor: '#009688'
}
}
export default IssueSelectionScreen;
Upvotes: 1
Views: 5502
Reputation: 65
For anyone else viewing this post, the answer above is very helpful. To add a few last remarks, if you're trying to get the buttons to light up I added a simple if else to selectMetric:
if (tempData[index].flag) {
tempData[index].buttonColor = '#03A9F4';
console.log('tempData true:', tempData);
} else {
tempData[index].buttonColor = null;
console.log('tempData false:', tempData);
}
and updated the backgroundColor property on Button in showMetric with:
backgroundColor={this.state.data[index].buttonColor}
Upvotes: 0
Reputation: 980
so the short answer to your question would look something like this:
class IssueSelectionScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: cloneDeep(RISK_DATA),
};
}
selectMetric = (index) => {
const tempData = cloneDeep(this.state.data);
tempData[index].flag = !tempData[index].flag;
this.setState({ data: tempData });
}
showMetric() {
return this.state.data.map((metric, index) => {
// same
})
}
render() {
// same
}
}
It involves putting the whole array of buttons into state since the state of those buttons is what can change. You could also maintain the flags as an array in state and keep the button info as a separate constant
This solution uses cloneDeep (from lodash) to prevent the code from mutating the state of the objects but you could probably also do it with this.state.data.map
and creating new objects (which works as long as your objects aren't deeply nested).
If you're using Redux, the list would probably come into the component as a prop, then selectMetric would be dispatching an action to update the flag in Redux.
Upvotes: 2