Search code examples
reactjsfirebasereact-nativereact-hooksuse-effect

useEffect not aware of state update


I am facing issue of duplicate element when updating the state in useEffect, WHat i am trying to do is I get data from firebase and iterate through the data and updating an array with useState, and if the element already exists in in the array, then skip to add that and move to next element. Codes are below

import React, {useState, useEffect} from 'react';
import {View, FlatList} from 'react-native';


const Users = props => {
const [users, setUsers] = useState([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
  (async () => {
      const dbUsers = await db.collection('users').get();
        if (dbUsers.length !== 0) {
          dbUsers.map((filteredUser) => {
            if (dbUsers.exists) {
             const isUserExists = users.find((u) => u.key === dbUsers.id);
              if (!isUserExists) {
                setUsers((prevState) => [
                 ...prevState,
                 new ContactsList(
                 dbUsers.id,
                 dbUsers.data().name,
                 dbUsers.data().number,
                 dbUsers.data().profileAvtar,
                 dbUsers.data().onesignalId,
                ),
             ]);
            }
          }
        });
      }
})();
}, [users]});

setUsers, not reflecting the updated state in mapping the array.


Solution

  • When you want to update an array of values, always prefer updating it with a single call to setUsers instead of appending values one at a time using multiple calls. With your current implementation, a new call to db.collection('users').get() is triggered for each user record it returns, leading to the duplication you're seeing.

    Try this:

    const Users = props => {
      const [users, setUsers] = useState([]);
      const [isLoading, setIsLoading] = useState(false);
    
      useEffect(() => {
        (async () => {
          const dbUsers = await db.collection('users').get();
          const updatedUsers = dbUsers.map(dbUser => {
            // look for an existing user
            const existingUser = users.find(u => u.key === dbUser.id);
    
            // if we already have the user locally, don't update
            if (existingUser) {
              return existingUser;
            }
    
            // otherwise, create and return a new user from the remote data
            return new ContactsList(
              dbUser.id,
              dbUser.data().name,
              dbUser.data().number,
              dbUser.data().profileAvtar,
              dbUser.data().onesignalId,
            );
          });
          setUsers(updatedUsers);
        })();
      }, [users]);
    }
    

    As @lissettdm pointed out, make sure ContactsList has a key field that is the same type (string or integer) as dbUser.id.