React Native Flat List (Infinite Scroll) is returning the duplicate records from Firebase

。_饼干妹妹 提交于 2021-01-29 11:18:18

问题


I was able to get Infinite Scroll working with React Native and Firebase, but there's an issue on retrieving the next 6 documents (limit is set to 6). It returns another 6 once the end of the list is refreshed, but it's the same 6 documents appended to the previous same 6 documents.

I have the startAt increase each time 6 records are rendering and the startAt is increasing at the right amount. Not sure if I'm missing something or if it could be an async issue?

// Imports: Dependencies
import React, { Component } from "react";
import { ActivityIndicator, Dimensions, FlatList, View, SafeAreaView, StyleSheet } from 'react-native';
import * as firebase from 'firebase';
import 'firebase/firestore';
import firebaseConfig from '../config/config';

// Imports: Components
import UserSelector from '../components/UserSelector';
import TitleLarge from '../components/TitleLarge';

// Screen Dimensions
const { height, width } = Dimensions.get('window');

// Screen: Flat List (Users)
class FlatListUsers extends Component {
  constructor(props) {
    super(props);

    this.state = {
      data: [],
      startAt: 0,
      limit: 6,
      loading: false,
    };
  }

  // Component Will Mount
  componentWillMount = () => {
    // Firebase: Initialize
    firebase.initializeApp({
      apiKey: `${firebaseConfig.apiKey}`,
      authDomain: `${firebaseConfig.authDomain}`,
      databaseURL: `${firebaseConfig.databaseURL}`,
      projectId: `${firebaseConfig.projectId}`,
      storageBucket: `${firebaseConfig.storageBucket}`,
      messagingSenderId: `${firebaseConfig.messagingSenderId}`,
    });
  }

  // Component Did Mount
  componentDidMount = () => {
    this.retrieveUsers();
  }

  // Retrieve Users
  retrieveUsers = async () => {
    try {
      // Set State: Loading
      this.setState({ loading: true });

      // Firebase: Database + Settings
      const db = firebase.firestore();

      // Query
      console.log('Fetching Users')
      const query = await db.collection('users')
                            .where('company', '==', 'Google')
                            .orderBy('first_name')
                            .startAt(this.state.startAt)
                            .limit(this.state.limit);

      // Query Snapshot
      const querySnapshot = await query.get();

      // Document Data
      console.log('Document Data');
      const documentData = querySnapshot.docs.map(document => document.data());
      // console.log(documentData);

      // Set State: Initial Query
      if (this.state.startAt <= this.state.limit) { 
        // Set State
        this.setState({
          data: documentData,
          startAt: this.state.startAt + this.state.limit + 1,
          loading: false,
          refreshing: false,
        })
      }
      // Set State: Refreshing Queries
      else {
        // Set State
        this.setState({
          data: [...this.state.data, ...documentData],
          startAt: this.state.startAt + this.state.limit + 1,
          loading: false,
          refreshing: false,
        })
      }

      // Start At
      console.log(`StartAt: ${this.state.startAt}`);
    }
    catch (error) {
      console.log(error);
    }
  };

  // Retrieve More Users
  retrieveMore = async () => {
    try {
      // Set State + Retrieve Users 
      this.setState({
        loading: true,
      }, async () => {
        await this.retrieveUsers();
      })

      // Set State
      this.setState({
        loading: false,
      })
    }
    catch (error) {
      console.log(error);
    }
  };

  // Render Header
  renderHeader = () => {
    try {
      return (
        <View style={styles.activityIndicator}>
          <TitleLarge title="Users" />
        </View>
      )
    }
    catch (error) {
      console.log(error);
    }
  };

  // Render Footer
  renderFooter = () => {
    try {
      // Check If Loading
      if (this.state.loading) {
        return <ActivityIndicator />
      }
      else {
        return null;
      }
    }
    catch (error) {
      console.log(error);
    }
  };

  render() {
    return (
      <SafeAreaView style={styles.container}>
        <FlatList
          data={this.state.data}
          renderItem={({ item }) => ( 
            <UserSelector
              key={(item, index) => {
                return item.id;
              }}
              firstName={item.first_name}
              lastName={item.last_name}
              company={item.company}
            />
          )}
          keyExtractor={( item ) => {
            return item.id;
          }}
          ListHeaderComponent={this.renderHeader}
          ListFooterComponent={this.renderFooter}
          onEndReached={this.retrieveMore}
          onEndReachedThreshold={0}
        />
      </SafeAreaView>
    )
  }
}

// Styles
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    height: height,
    width: width,
    borderTopWidth: 0,
    borderBottomWidth: 0,
  },
  scrollView:{
    height: 'auto',
  },
  UserContainer: {
    width: width,
    marginBottom: 7,
  },
  itemText: {
    fontFamily: 'System',
    fontSize: 17,
    fontWeight: '400',
    color: '#222222',
    marginLeft: 16,
  },
  activityIndicator: {
    paddingVertical: 20,
    borderTopWidth: 0,
    borderTopColor: '#CED0CE',
  },
});

// Exports
export default FlatListUsers

回答1:


Flatlist may call your retrieval method twice. So be sure to use the loading prop to prevent the method from running.

onEndReached={(foo)=>{
  if (this.state.loading === false){
   this.makeAPizza(foo);
 }
}}



回答2:


The query gets the same results because it always starts at the same offset: startAt: 0.

To fix, keep a pageNumber in state, advanced as the user scrolls, then startAt: pageNumber*6

A few other comments about the code: State can be simplified...

this.state = {
  data: [],
  limit: 6,
  // startAt removed.  start at the end of data
  loading: false
};

Don't need retrieveMore. Its the same as retrieveUsers. retrieveUsers can be simplified...

// Retrieve Users
retrieveUsers = async () => {
  try {
    // Set State: Loading
    this.setState({ loading: true });

    // Firebase: Database + Settings
    const db = firebase.firestore();

    // Query
    console.log('Fetching Users')
    const query = await db.collection('users')
                          .where('company', '==', 'Google')
                          .orderBy('first_name')
                          .startAt(this.state.data.length)
                          .limit(this.state.limit);

    // Query Snapshot
    const querySnapshot = await query.get();

    // Document Data
    console.log('Document Data');
    const documentData = querySnapshot.docs.map(document => document.data());
    // console.log(documentData);


    // Set State
    this.setState({
      data: [...this.state.data, ...documentData],
      loading: false
    })
  }
  catch (error) {
    console.log(error);
  }
};

Notice that startAt is computed to be the length of data already retrieved. This works when data array is empty, also. Notice that logic to update state after the get is the same for the first or nth get: append new data to the existing data.

You don't need retrieveMore. Its the same as retrieveUsers. retrieveUsers can be simplified...

// ...
onEndReached={this.retrieveUsers}


来源:https://stackoverflow.com/questions/55959697/react-native-flat-list-infinite-scroll-is-returning-the-duplicate-records-from

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!