-1

My issue is very simple. I have a firebase realtime database and i am fetching data from the database and trying to insert in inside a useState so that i can then display this data in my app. But no matter what i do, the data is always undefined or null. Strangely enough if i insert that same exact data inside a jsx component or console log it, it displays without any problem.

Here's what i am doing and a description of the problem:

First here is my realtime-database with users

enter image description here

I simply want to access the fullname property and insert that inside my 'name' state

App.js

const userid = firebase.auth().currentUser.uid
const [usersList , setUsersList ] = useState([])
const [name, setName] = useState(usersList.fullname) //undefined

useEffect(() => {
const usersRef = firebase.database().ref('Users')
let query = usersRef.orderByChild('uid').equalTo(userid); 
query.on('value', (snapshot) => {
  const users = snapshot.val() 
  const usersList = []
  for (let id in users) {
    usersList.push({id, ...users[id]}) 
  }  
  setUsersList(usersList[0]) 
})
},[])

Displaying data:

console.log(usersList.fullname) //displays my name perfectly well

And

return (
<div>{usersList.fullname}</div>
)

Also displays perfectly well

But

const [name, setName] = useState(usersList.fullname) //undefined

This seems completely illogical to me! If yo have any insight on why this is happening, share it with me please.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Aviale
  • 117
  • 1
  • 5
  • Why are you making a second call to `useState` when you are putting what you need in the first state hook? Wouldn't you just want to keep using `usersList`? Also it's not clear to me why you are setting a single object value into something called a "list". `usersList` only contains a list initially. After that, it only contains a single object value. – Doug Stevenson Dec 27 '20 at 18:57
  • Its kind of irrelevant for this issue. We can discuss react best practices later but according to my logic - everything is working as intended except displaying it inside the useState. Wouldn't you agree? – Aviale Dec 27 '20 at 19:25
  • In a general sense, code is a way for humans to communicate to other humans about what the computer is supposed to do. When that communication breaks down and becomes difficult to discern intent, it also becomes difficult to actually discuss what the computer is actually supposed to do. – Doug Stevenson Dec 27 '20 at 19:30

2 Answers2

2

The initial value passed to useState is only used during the first rendering of the functional component, but at that point usersList is an empty array, so [].fullname is undefined. After that you must use setName to change the value of that useState, so you could call:

setUsersList(usersList[0]) 
setName(usersList[0].fullname)
Ric
  • 8,615
  • 3
  • 17
  • 21
1

What you're seeing is the expected behavior. Loading data from Firebase (just like calling most cloud APIs) is an asynchronous operation. That's why you're passing a function (snapshot) => {...} into the operation, so that it can call you back when the operation has completed.

Until that happens, none of the code inside that function has run, so usersList will have its default value. And since you initialize the state as useState([]) its default value is [], which doesn't have a fullname member.

My best guess it that you want to set the username too after setting usersList. So:

setUsersList(usersList[0]);
setName(usersList[0].fullname);

Unrelated: it is more idiomatic to store the users under their UID. , instead of with push() as you do now. By using the UID as the key, you:

  1. Automatically ensure that each UID can exist only once, since keys are by definition unique in a node.
  2. Can load the user with a given UID without needing a query, which scales better if you ever reach hundreds of thousands of users.
  3. Don't need to loop in the callback anymore.

So in that case, loading the user/name becomes:

useEffect(() => {
  const usersRef = firebase.database().ref('Users')
  let query = usersRef.child(userid); 
  query.on('value', (snapshot) => {
    const user = snapshot.val() 
    setUsersList(user);
    setName(user.fullname);
  })
},[])
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • So how do you explain the fullname variable working in a jsx component? What you are saying only makes sense to ME if it wouldn't be working inside jsx nor console log. – Aviale Dec 27 '20 at 19:29
  • 1
    When you modify the state (as `setUsersList` does) it cause the JSX to be re-rendered. But it doesn't cause the `const [name, setName] = useState(usersList.fullname) //undefined` to be rerun. That's why the JSX shows the update at that point, but `name` is still `undefined`. – Frank van Puffelen Dec 27 '20 at 19:31
  • Hmm i copied your code and replaced it into mine but i'm getting error that value of fullname of user is undefined, But if i just edit my original code and add setName(usersList[0].fullname) it works. Any ideas? – Aviale Dec 27 '20 at 19:46
  • Not really. It's hard for me to say what the difference is between your tries and the code in my answer in that case. – Frank van Puffelen Dec 27 '20 at 23:15