'React Native FlatList with custom buttons, how to change color on click?
I have created a FlatList that renders names. Each row has its own button. When you click the button, the name is added to a list and the button should change color
My problem is that the button does not change color as soon as I click on it, it only changes color when I make a pull refresh on my FlatList
Use my snack: https://snack.expo.io/@thesvarta/92f850
This is my component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
View,
Text,
FlatList,
StatusBar,
StyleSheet,
ActivityIndicator,
TouchableOpacity,
TouchableHighlight,
Image,
Button,
SearchBar,
TextInput,
} from 'react-native';
import TextDetails from '../TextDetails/TextDetails';
import { selectOptions } from '../store/actions/index';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
error: null,
refreshing: false,
groupBy: '',
};
}
setFetch = () => {
this.setState(
{
groupBy: this.props.GroupBy,
},
() => {
this.makeRemoteRequest();
}
);
};
componentDidMount() {
this.setFetch();
}
makeRemoteRequest = () => {
const { page, groupBy } = this.state;
const url = `api/getOptions.php?page=${page}&row_per_page=5&group_by=${groupBy}`;
fetch(url)
.then(response => response.json())
.then(responseJson => {
this.setState(
{
data:
page == 1
? responseJson[0].DATA
: [...this.state.data, ...responseJson[0].DATA],
status: responseJson[0].STATUS,
message: responseJson[0].MESSAGE,
isLoading: false,
refreshing: false,
},
() => {}
);
});
};
handleRefresh = () => {
this.setState(
{
page: 1,
refreshing: true,
},
() => {
this.makeRemoteRequest();
}
);
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1,
},
() => {
this.makeRemoteRequest();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
inArray = (needle, haystack) => {
let length = haystack.length;
for (let i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
};
renderRow = ({ item, index }) => {
return (
<View style={styles.ListContainer}>
<View style={styles.Text}>
<TextDetails Size={18}>{item.name}</TextDetails>
</View>
<View style={styles.button}>
<TouchableOpacity
style={
this.inArray(item.name, this.props.selectedOptions)
? styles.buttonOrange
: styles.buttonGray
}
onPress={() => this.props.onSelectOptions(item.name)}
/>
</View>
</View>
);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<View style={styles.SearchContatiner}>
<View style={styles.Search}>
<View style={styles.ImageIcon}>
<Image
style={styles.Image}
resizeMode="contain"
source={require('../images/sokbla.png')}
/>
</View>
<View style={styles.InputBox}>
<TextInput
style={styles.InputText}
onChangeText={text => this.setState({ query: text })}
placeholder={'Search for ' + this.props.Title}
value={this.state.query}
/>
</View>
<TouchableOpacity
onPress={() => alert(this.props.selectedOptions)}
style={styles.KryssIcon}>
<Image
style={styles.ImageKryss}
resizeMode="contain"
source={require('../images/kryssbla.png')}
/>
</TouchableOpacity>
</View>
<View style={styles.Lista}>
<FlatList
style={styles.Flatlist}
data={this.state.data}
renderItem={this.renderRow}
keyExtractor={(item, index) => index.toString()}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
button: {
width: '20%',
justifyContent: 'center',
alignItems: 'center',
},
buttonGray: {
width: 25,
height: 25,
borderRadius: 12.5,
backgroundColor: 'gray',
},
buttonOrange: {
width: 25,
height: 25,
borderRadius: 12.5,
backgroundColor: 'orange',
},
Lista: {
marginTop: 20,
},
Flatlist: {
width: '100%',
height: 300,
},
ListContainer: {
flexDirection: 'row',
width: '100%',
height: 40,
},
Text: {
marginLeft: '10%',
width: '70%',
justifyContent: 'center',
},
SearchContatiner: {},
Search: {
width: '100%',
height: 60,
backgroundColor: 'rgb(240,240,240)',
flexDirection: 'row',
},
Image: {
width: 23,
height: 33,
},
ImageIcon: {
justifyContent: 'center',
width: '15%',
alignItems: 'center',
},
InputBox: {
width: '70%',
justifyContent: 'center',
},
InputText: {
paddingLeft: 20,
width: '100%',
height: 50,
fontSize: 20,
},
KryssIcon: {
justifyContent: 'center',
width: '15%',
alignItems: 'center',
},
ImageKryss: {
width: 18,
height: 28,
},
});
const mapStateToProps = state => {
return {
selectedOptions: state.filter.selectedOptions,
};
};
const mapDispatchToProps = dispatch => {
return {
onSelectOptions: name => dispatch(selectOptions(name)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Search);
This is my Action
import { OPTIONS_SELECT } from './actionTypes';
export const selectOptions = name => {
return {
type: OPTIONS_SELECT,
selectedOptions: name,
};
};
And my reducer
import {
OPTIONS_SELECT,
} from '../actions/actionTypes';
const initialState = {
selectedOptions:[],
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case OPTIONS_SELECT:
return {
...state,
selectedOptions: [...state.selectedOptions, action.selectedOptions],
};
default:
return state;
}
};
export default reducer;
Solution 1:[1]
The extraData
property of the FlatList
component is missing:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
LInk to the docs: https://facebook.github.io/react-native/docs/flatlist
This property updates the FlatList
when the passed object is updated. In your case you need to pass:
extraData={this.props.selectedOptions}
Here is a working demo of your snack.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Hristo Eftimov |