'React Native FlatList gets infinity loop onRefresh
I'm trying to fetch data from redux to a FlatList and onRefresh={} gives me an infinity loop. When I don't use redux it works but when I moved my fetching into a redux action I'm getting confused.
Can someone describe to me what I am doing wrong?
Thanks in advance.
use my snack: https://snack.expo.io/@thesvarta/redux-with-flatlist
Heres 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 {
fetchDataOptions,
resetDataOptions,
selectOptions,
} from '../store/actions/index';
class Search extends Component {
constructor(props) {
super(props);
this.state = {
page: 1,
loading: false,
refreshing: false,
};
}
componentDidMount() {
this.props.onResetDataOptions();
this.props.onFetchDataOptions(this.state.page, this.props.GroupBy);
console.log('component_data', this.props.fetch_data_options);
}
handleRefresh = () => {
this.props.onResetDataOptions(); // Resets fetch_data_options
this.setState(
{
page: 1,
refreshing: true,
},
() => {
this.props.onFetchDataOptions(this.state.page, this.props.GroupBy);
}
);
};
handleLoadMore = () => {
if (!this.props.fetch_data_error) {
this.setState(
{
page: this.state.page + 1,
},
() => {
this.props.onFetchDataOptions(this.state.page, this.props.GroupBy);
console.log('load_more', this.state.page);
}
);
} else {
this.setState(
{
page: 1,
},
() => {
this.props.onFetchDataOptions(this.state.page, this.props.GroupBy);
console.log('load_more_error', this.state.page);
}
);
}
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '100%',
backgroundColor: '#CED0CE',
}}
/>
);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: '#CED0CE',
}}>
<ActivityIndicator animating size="large" />
</View>
);
};
renderRow = ({ item, index }) => {
return (
<View style={styles.ListContainer}>
<View style={styles.Text}>
<TextDetails Size={18}>{item.name}</TextDetails>
</View>
</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("remove query")}
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.props.fetch_data_options}
renderItem={this.renderRow}
keyExtractor={item => item.name}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
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 {
fetch_data_options: state.filter.fetch_data_options,
fetch_data_error: state.filter.error,
status: state.filter.status,
};
};
const mapDispatchToProps = dispatch => {
return {
onFetchDataOptions: (page, group_by) =>
dispatch(fetchDataOptions(page, group_by)),
onResetDataOptions: () => dispatch(resetDataOptions()),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Search);
Here is My Action
import {
FETCHING_DATA_OPTIONS,
FETCH_DATA_OPTIONS_SUCCESS,
FETCH_DATA_OPTIONS_FAILURE,
FETCH_DATA_OPTIONS_RESET,
} from './actionTypes';
export const fetchDataOptions = (page, group_by) => {
return dispatch => {
dispatch(getDataOptions());
return fetch(
'https://api/getOptions.php?page=' +
page +
'&row_per_page=5&group_by=' +
group_by
)
.then(res => res.json())
.then(json => {
//$json[0] containts .DATA, .STATUS, .MESSAGE
//.STATUS can be 200 or 404, if its 404 it means that .DATA is empty
if (json[0].STATUS == 200) {
return dispatch(
getDataOptionsSuccess(json[0]),
console.log('fetched_data', json[0])
);
} else {
return dispatch(getDataOptionsFailure());
}
})
.catch(err => dispatch(getDataOptionsFailure(err)));
};
};
export const resetDataOptions = () => {
return {
type: FETCH_DATA_OPTIONS_RESET
};
};
function getDataOptions() {
return {
type: FETCHING_DATA_OPTIONS,
};
}
function getDataOptionsSuccess(data) {
return {
type: FETCH_DATA_OPTIONS_SUCCESS,
data: data.DATA,
status: data.STATUS
};
}
function getDataOptionsFailure() {
return {
type: FETCH_DATA_OPTIONS_FAILURE,
};
}
Solution 1:[1]
You haven't changed the value of refreshing
state variable after dispatching the API call. It's set as true
in your handleRefresh
function but never set back to false
.
Use one of the lifecycle methods componentWIllUpdate
or componentDidUpdate
to check whether the props have changed (API call success or fail), and change the state of your refreshing
variable to false.
For eg. in your Search component add this,
componentDidUpdate(prevProps) {
//Check whether props have changed and isFetching is false
if (this.props.isFetching !== prevProps.isFetching && !this.props.isFetching) {
this.setState({refreshing:false});
}
}
Also check the value of onEndReachedThreshold
is 0. Usage is explained here
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 | Nishant Nair |