Syzon
Syzon

Reputation: 29

How to update a useState-hook with a condition in react?

I'm using react hooks (useEffect and useState) with firebase.

I have a collection of users which can be getted from firebase without any problems. The simplified code looks like this:

const [users, setUsers] = useState([]);

  useEffect(() => {
    const unsubscribe = database.collection("users").onSnapshot((snapshot) => {
      snapshot.forEach((doc) => {
        const currentUser = {
          id: doc.id,
          ...doc.data(),
        };

        setUsers((oldUsers) => [...oldUsers, currentUser]);
      });
    });

    return () => {
      unsubscribe();
    };
  }, []);

So far, so good.

If I change some properties of a user now, firebase fires and the useEffect-hook is triggered (thats what I want).

The problem here: through this firing my users array (useState) gets updated and gets pushed the same users again.

What I want to achieve now is the following:

I only want each user once in my users array. So that only new users should be added to this array. I think I need a conditional check in the setUsers-method.

I thought about something like this:

  setUsers(
          (oldUsers) =>
          {
            if (oldUsers.indexOf(currentUser) === -1) {
              oldUsers.push(currentUser);
            }
            
            // HERE I'M NOT SURE WHAT TO DO
            // how can I set my users to the updated oldUsers?
            // I made some first steps with those approaches...
            1) return oldUsers; 
            2) setUsers(oldUsers);
            3) [...oldUsers]
          }
        );

As mentioned in comments in the code: how can I update/set my users to a newer version (in case if a new user is inserted) or just let the users as they are (without adding them again)?

I'm thankful for any advice!

Upvotes: 2

Views: 2340

Answers (1)

raina77ow
raina77ow

Reputation: 106385

Avoid mutating the state indirectly (with Array.push or any other way), it's not consistent. Instead return the original state if there's no change, and a new object if there's one. For example:

setUsers(oldUsers => {
  if (oldUsers.find(user => user.id === currentUser.id)) 
    return oldUsers;
  return [...oldUsers, currentUser];
});

In this particular case you might want to avoid calling setUsers altogether if there's no change, as state itself is available (and you can check user's presence before).


As a sidenote, indexOf check won't work in this case, as you create a new object (from the data you've got in a snapshot). That's why it's predicate-based find in my example.

Upvotes: 1

Related Questions