Javascript React Native Flat List(无限滚动)正在从Firebase返回重复记录

Javascript React Native Flat List(无限滚动)正在从Firebase返回重复记录,javascript,reactjs,firebase,react-native,google-cloud-firestore,Javascript,Reactjs,Firebase,React Native,Google Cloud Firestore,我能够使用React Native和Firebase获得无限滚动,但是在检索接下来的6个文档时出现了一个问题(限制设置为6)。刷新列表末尾后,它将返回另一个6,但它是附加到先前相同6个文档的相同6个文档 每次渲染6条记录时,我都会增加startAt,并且startAt会以正确的数量增加。不确定我是否遗漏了什么,或者是否可能是异步问题 // Imports: Dependencies import React, { Component } from "react"; import { Activi

我能够使用React Native和Firebase获得无限滚动,但是在检索接下来的6个文档时出现了一个问题(限制设置为6)。刷新列表末尾后,它将返回另一个6,但它是附加到先前相同6个文档的相同6个文档

每次渲染6条记录时,我都会增加startAt,并且startAt会以正确的数量增加。不确定我是否遗漏了什么,或者是否可能是异步问题

// 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
//导入:依赖项
从“React”导入React,{Component};
从“react native”导入{ActivityIndicator,Dimensions,FlatList,View,SafeAreaView,StyleSheet};
从“firebase”导入*作为firebase;
导入“firebase/firestore”;
从“../config/config”导入firebaseConfig;
//进口:组件
从“../components/UserSelector”导入UserSelector;
从“../components/TitleLarge”导入标题栏;
//屏幕尺寸
const{height,width}=Dimensions.get('window');
//屏幕:平面列表(用户)
类FlatListUsers扩展组件{
建造师(道具){
超级(道具);
此.state={
数据:[],
startAt:0,
限额:6,
加载:false,
};
}
//组件将安装
组件将装入=()=>{
//Firebase:初始化
firebase.initializeApp({
apiKey:`${firebaseConfig.apiKey}`,
authDomain:`${firebaseConfig.authDomain}`,
databaseURL:`${firebaseConfig.databaseURL}`,
projectId:`${firebaseConfig.projectId}`,
storageBucket:`${firebaseConfig.storageBucket}`,
messagingSenderId:“${firebaseConfig.messagingSenderId}”,
});
}
//组件没有安装
componentDidMount=()=>{
this.retrieveUsers();
}
//检索用户
retrieveUsers=async()=>{
试一试{
//设置状态:加载
this.setState({loading:true});
//Firebase:数据库+设置
const db=firebase.firestore();
//质疑
console.log('Fetching Users')
const query=await db.collection('用户')
.where('company','=','Google')
.orderBy('first_name')
.startAt(this.state.startAt)
.limit(此.state.limit);
//查询快照
const querySnapshot=wait query.get();
//文件数据
console.log(“文档数据”);
const documentData=querySnapshot.docs.map(document=>document.data());
//控制台日志(文档数据);
//设置状态:初始查询
如果(this.state.startAt){
试一试{
//设置状态+检索用户
这是我的国家({
加载:对,
},异步()=>{
等待此消息。retrieveUsers();
})
//设定状态
这是我的国家({
加载:false,
})
}
捕获(错误){
console.log(错误);
}
};
//渲染头
renderHeader=()=>{
试一试{
返回(
)
}
捕获(错误){
console.log(错误);
}
};
//渲染页脚
renderFooter=()=>{
试一试{
//检查是否已加载
if(this.state.loading){
返回
}
否则{
返回null;
}
}
捕获(错误){
console.log(错误);
}
};
render(){
返回(
( 
{
返回item.id;
}}
firstName={item.first_name}
lastName={item.last_name}
公司={item.company}
/>
)}
keyExtractor={(项目)=>{
返回item.id;
}}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onEndReached={this.retrieveMore}
onEndReachedThreshold={0}
/>
)
}
}
//风格
const styles=StyleSheet.create({
容器:{
弹性:1,
为内容辩护:“中心”,
高度:高度,,
宽度:宽度,
borderTopWidth:0,
边框底部宽度:0,
},
滚动视图:{
高度:“自动”,
},
用户容器:{
宽度:宽度,
marginBottom:7,
},
项目文本:{
fontFamily:“系统”,
尺寸:17,
fontWeight:'400',
颜色:“#2222222”,
marginLeft:16,
},
活动指示器:{
填充垂直:20,
borderTopWidth:0,
边框颜色:“#CED0CE”,
},
});
//出口
导出默认FlatListUsers

查询得到相同的结果,因为它总是从相同的偏移量开始:
startAt:0

要修复此问题,请将
pageNumber
保持在用户滚动时的高级状态,然后
startAt:pageNumber*6

关于代码的其他一些注释:状态可以简化

this.state = {
  data: [],
  limit: 6,
  // startAt removed.  start at the end of data
  loading: false
};
// 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);
  }
};
// ...
onEndReached={this.retrieveUsers}
不需要
retrieveMore
。它与
retrieveUsers
相同
retrieveUsers
可以简化

this.state = {
  data: [],
  limit: 6,
  // startAt removed.  start at the end of data
  loading: false
};
// 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);
  }
};
// ...
onEndReached={this.retrieveUsers}
请注意,startAt被计算为已检索数据的长度。这在数据数组为空时也适用。请注意,
get
之后更新状态的逻辑与第一个或第n个get相同:将新数据附加到现有数据

您不需要
retrieveMore
。它与
retrieveUsers
相同
retrieveUsers
可以简化

this.state = {
  data: [],
  limit: 6,
  // startAt removed.  start at the end of data
  loading: false
};
// 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);
  }
};
// ...
onEndReached={this.retrieveUsers}

Flatlist
可能会调用您的检索方法两次。因此请确保使用
加载
属性来阻止该方法运行

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

我已经让startAt处于状态(this.state.startAt),并且每次函数执行时它都会更新6。因此startAt从0变为6,依此类推。我是否遗漏了一些内容?抱歉,我遗漏了您推进startAt的逻辑。更仔细地回顾一下,有一些简化可以使代码更易于调试(至少,它可能会发现一个修复)。一个关键的想法是startAt不需要(我认为不应该)提前计算。始终从存在的数据的末尾索引开始。