简体   繁体   中英

React Native: Stop endlesss loop of onEndReached of FlatList when empty json array is retuned from rest api

I am using the infinite scrolling Flatlist pattern of react-native mentioned on the internet. So far it's good for having long data. But when the rest API pagination completed, it returns the empty JSON array. Now with infinite scrolling pattern, onEndReached function is infinitely thrown leading to unnecessary rest call. So what's the best way to deal with this situation.

The data from API could be a single row or 500 rows of JSON object depending upon the conditional URL mentioned in code. In the case of 500 rows, it's fine till there's more data to fetch but after the last 480-500 batch, it becomes an infinite loop, which is a problem. In the case of 1 row, it immediately becomes an endless loop. How could I break the onEndReached event conditionally so that it never trigger when I detect empty array from rest api.

Below is my Flatlist implementation:

import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, ActivityIndicator, TouchableOpacity, ToastAndroid } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import GLOBAL from './productglobal'
import { Ionicons } from '@expo/vector-icons';
import SearchBar from '../commoncomponents/searchbar'

export default class ProductsList extends Component {

    static navigationOptions = ({ navigation }) => {
        return {
            headerTitle: 'Products',
            headerRight: () => (
                <TouchableOpacity
                    style={{ paddingRight: 20 }}
                    onPress={() => { navigation.navigate("Settings") }}
                >
                    <Ionicons name='md-more' size={25} color='white' />
                </TouchableOpacity>
            ),
        }
    };

    constructor(props) {
        super(props);
        this.state = {
            loading: false,
            searchValue: '',
            data: [],
            page: 1,
            error: null,
            refreshing: false,
            base_url: null,
            c_key: null,
            c_secret: null,
        };
        GLOBAL.productlistScreen = this;
    }

    async componentDidMount() {
        await this.getCredentials();
        this.fetchProductList();
    }

    getCredentials = async () => {
        const credentials = await SecureStore.getItemAsync('credentials');
        const credentialsJson = JSON.parse(credentials)
        this.setState({
            base_url: credentialsJson.base_url,
            c_key: credentialsJson.c_key,
            c_secret: credentialsJson.c_secret,
        })
    }

    fetchProductList = () => {
        const { base_url, c_key, c_secret, page, searchValue } = this.state;
        let url = null
        if (searchValue) {
            url = `${base_url}/wp-json/wc/v3/products?per_page=20&search=${searchValue}&page=${page}&consumer_key=${c_key}&consumer_secret=${c_secret}`;
        } else {
            url = `${base_url}/wp-json/wc/v3/products?per_page=20&page=${page}&consumer_key=${c_key}&consumer_secret=${c_secret}`;
        }
        console.log(url)
        this.setState({ loading: true });
        setTimeout(() => {
            fetch(url).then((response) => response.json())
                .then((responseJson) => {
                    this.setState({
                        data: this.state.data.concat(responseJson),
                        error: responseJson.code || null,
                        loading: false,
                        refreshing: false
                    });
                }).catch((error) => {
                    this.setState({
                        error,
                        loading: false,
                        refreshing: false
                    })
                });
        }, 1500);
    };

    renderListSeparator = () => {
        return (
            <View style={{
                height: 1,
                width: '100%',
                backgroundColor: '#999999'
            }} />
        )
    }

    renderListFooter = () => {
        if (!this.state.loading) return null;

        return (
            <View style={{
                paddingVertical: 20,
            }}>
                <ActivityIndicator color='#96588a' size='large' />
            </View>
        )
    }

    handleRefresh = () => {
        this.setState({
            page: 1,
            refreshing: true,
            data: []
        }, () => {
            this.fetchProductList();
        }
        )
    }

    handleLoadMore = () => {
        console.log('loading triggered')
        this.setState({
            page: this.state.page + 1,
        }, () => {
            this.fetchProductList();
        })
    }

    handleSearch = (value) => {
        // console.log(value)
        this.setState({
            searchValue: value,
            page: 1,
            refreshing: true,
            data: []
        }, () => {
            this.fetchProductList()
        })
    }

    render() {
        return (
            <View style={{flex:1}}>
                <SearchBar onSearchPress={this.handleSearch}></SearchBar>
                <FlatList
                    data={this.state.data}
                    keyExtractor={item => item.id.toString()}
                    refreshing={this.state.refreshing}
                    extraData={this.state.data}
                    onRefresh={this.handleRefresh}
                    onEndReached={this.handleLoadMore}
                    onEndReachedThreshold={0.5}
                    ItemSeparatorComponent={this.renderListSeparator}
                    ListFooterComponent={this.renderListFooter}
                    renderItem={({ item }) =>
                        <TouchableOpacity onPress={() => {
                            this.props.navigation.navigate('ProductDetails', {
                                productId: item.id,
                                productName: item.name,
                                base_url: this.state.base_url,
                                c_key: this.state.c_key,
                                c_secret: this.state.c_secret
                            });
                        }}>
                            <View style={{ flex: 1, flexDirection: 'row', backgroundColor: 'white' }}>
                                <View style={{ flex: 1, justifyContent: "center", alignContent: "center" }}>
                                    <Image source={(Array.isArray(item.images) && item.images.length) ?
                                        { uri: item.images[0].src } :
                                        require('../../../assets/images/blank_product.png')}
                                        onError={(e) => { this.props.source = require('../../../assets/images/blank_product.png') }}
                                        style={{ height: 115, width: 115 }} resizeMode='contain' />
                                </View>
                                <View style={{ flex: 2, marginTop: 10, marginBottom: 10, justifyContent: "center" }}>
                                    <View style={{ marginLeft: 10 }}>
                                        <Text style={styles.titleText}>{item.name}</Text>
                                        <Text>SKU: {item.sku}</Text>
                                        <Text>Price: {item.price}</Text>
                                        <Text>Stock Status:  {item.stock_status}</Text>
                                        <Text>Stock: {item.stock_quantity}</Text>
                                        <Text>Status: {item.status}</Text>
                                    </View>
                                </View>
                            </View>
                        </TouchableOpacity>
                    }
                />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    titleText: {
        fontSize: 20,
        fontWeight: 'bold',
    }
});

You can add a state property hasMoreToLoad which defaults to true.

Then you can check in fetchProductList if the result is less than per_page (data < 20) IF the result is less than per_page you know you've reached the end and you can set hasMoreToLoad to false.

onEndReached={this.state.hasMoreToLoad ? this.handleLoadMore : null}

Just simply put condition on onEndReached() :

<FlatList
  data={this.state.data}
  keyExtractor={item => item.id.toString()}
  refreshing={this.state.refreshing}
  extraData={this.state.data}
  onRefresh={this.handleRefresh} 
  onEndReached={this.status.data.length > 0 ? this.handleLoadMore : null} // change here
  ...
  ...
/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM