Search code examples
reactjsfirebasereact-nativegoogle-cloud-firestorereact-native-elements

Is there a way to update a flatlist when there's a change in firestore database?


I have a list that displays user profiles, names, and the last message sent. Pretty much in a way a regular messaging app displays users. What I'm trying to do is have the list update when there's a change from the users being displayed. I tried having it update on render but with resetting states, it goes onto an infinite loop which brought up my read operations to 10k in a matter of seconds. So far I have it update with pull to refresh but I want it to update live. I'm not sure if I would need to use cloud functions (e.g. onCreate) or a timer to not quickly go over my quota limit.

import React, { Component } from "react";
import { View, FlatList } from "react-native";
import { ListItem } from "react-native-elements";
import fireStoreDB from "../database/FirestoreDB";

export default class Home extends Component {
  constructor(props) {
    super(props);
    this.state = {
      usersInfo: [],
      refreshing: false
    };
  }

  componentDidMount() {
    this.LoadUsers();
  }

  LoadUsers = () => {
    fireStoreDB
      .getAllUsersExceptCurrent()
      .then(
        users =>
          Promise.all(
            users.map(
              ({ id, username, avatar }) =>
                fireStoreDB
                  .getUserLastMessage(fireStoreDB.getUID, id)
                  .then(message => ({ id, username, avatar, message }))
            )
          )
      )
      .then(users => {
        this.setState({
          usersInfo: users.filter(x => typeof x.avatar !== "undefined"),
          refreshing: false
        });
      });
  };

  renderItem = ({ item }) => (
    <ListItem
      onPress={() => {
        this.props.navigation.navigate("Chat", {
          userTo: item.id,
          UserToUsername: item.username,
          LoadUsers: this.LoadUsers
        });
      }}
      title={item.username}
      subtitle={item.message}
      leftAvatar={{ source: { uri: item.avatar } }}
      bottomDivider
      chevron
    />
  );

  render() {
    return (
      <View>
        <FlatList
          data={this.state.usersInfo}
          renderItem={this.renderItem}
          keyExtractor={item => item.id}
          refreshing={this.state.refreshing}
          onRefresh={() => {
            this.setState({ refreshing: true });
            this.LoadUsers();
          }}
        />
      </View>
    );
  }
}

Solution

  • I solved it by doing this.

      async componentDidMount() {
        await Font.loadAsync({
          "open-sans-semi-bold": require("../assets/fonts/OpenSans-SemiBold.ttf"),
          Roboto: require("../node_modules/native-base/Fonts/Roboto.ttf"),
          Roboto_medium: require("../node_modules/native-base/Fonts/Roboto_medium.ttf"),
          ...Ionicons.font
        });
    
        this.unsubscribeMsg = fireStoreDB.lastMsgListener(this.LoadUsers);
        this.unsubscribeUser = fireStoreDB.userProfileListener(this.LoadUsers);
        this.setState({ isReady: true });
      }
    
      componentWillUnmount() {
        this.unsubscribeUser();
        this.unsubscribeMsg();
      }
    
      lastMsgListener = loadUsersCallback => {
        return firebase
          .firestore()
          .collectionGroup("chats")
          .onSnapshot(() => {
            loadUsersCallback();
          });
      };
    
      userProfileListener = loadUsersCallback => {
        return firebase
          .firestore()
          .collection("users")
          .onSnapshot(() => {
            loadUsersCallback();
          });
      };