Fotios Tsakiris
Fotios Tsakiris

Reputation: 1556

React renders only one item from list downloaded from firebase

I'm trying to show a list of users, I get from Firebase Realtime Database. On the logs I get them all! But on the screen, only the first one appears. If I put as a dependency users I get an infinite loop on the logs but still one item on the screen. useCallback didn't help either. If I fill in the inputs on the screen, thus trigger a re-render, then I get all the users. Why doesn't it get them all on the first place? Could it be something with Typescript?

Thanks!

...
const [users, setUsers] = useState<any[]>([]);
const [usersRef] = useState<any>(firebase.database().ref().child('users'));

...
const getUsers: () => void = useCallback(() => {
    const loadedUsers: any[] = []
    usersRef.on('child_added', (snap: any) => {
      loadedUsers.push(snap.val())
      setUsers(loadedUsers)
      console.log('loadedUsers', loadedUsers);
    })
  }, [setUsers, usersRef])

  useEffect(() => {
    getUsers();
    return () => usersRef.off()
  }, [usersRef, getUsers])


  const displayUsers = (users: any) => {
    console.log(users);
    return users.length > 0 && users.map((user: any) =>
      <li key={user.id}>
        {user.email}
      </li>
    )
  }
...

  <ul>
    {displayUsers(users)}
  </ul>

So, thanks to @Renaud Tarnec, it turned out to be a problem with the firebase listener. I had to listen on value change instead of child_added. Below is the new version. I don't really like the way displayUsers turned out to be, but it works. If any has any suggestions, I would be happy to see.

Thanks again!

  const getUsers: () => void = useCallback(() => {
    const loadedUsers: any[] = []
    usersRef.on('value', (snap: any) => {
      loadedUsers.push(snap.val())
      setUsers(loadedUsers)
      console.log('loadedUsers', loadedUsers);
    })
  }, [setUsers, usersRef])
...


const displayUsers = (users: any) => {
    console.log(users);
    return users.length > 0 && users.map((userObj: any) => {
      const users = []
      for (const key in userObj) {
        users.push(userObj[key])
      }
      return users.map((user: any) =>
        <li key={user.id}>
          {user.email}
        </li>
      )
    }
    )
  }

Upvotes: 0

Views: 50

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83163

If I correctly understand your problem, this is because you use the child_added event instead of the value one. See the difference between the two in the doc:

value event

This event will trigger once with the initial data stored at this location, and then trigger again each time the data changes. The DataSnapshot passed to the callback will be for the location at which on() was called..

child_added event

This event will be triggered once for each initial child at this location, and it will be triggered again every time a new child is added. The DataSnapshot passed into the callback will reflect the data for the relevant child.

In summary, with the value event you get all the data at this location, while with the child_added event you get the data of the child.

Upvotes: 1

Related Questions