1

I fetched some data from my firebase realtime database which is returned as an object inside my useEffect hook function. I wanted to map these data into different rows in my UI. But the problem is when I try to setState in the component by passing the fetched data in the setState() function, it returns an empty object. I tried to convert the fetched data object to convert to an array and then set the state, still, the console.log(state) shows an empty array. When I use the state as a dependency (2nd argument in the useEffect) it returns an infinite loop of the state. The code looks like this-

const ToDoList = () => {
   const [toDo, setToDo] = useState([])

   useEffect(() =>  {
    const fetchData = async () => {
      try{
        toDoListRef.on('value', snapshot => {
          const fetchedPostObject = snapshot.val()
          console.log(fetchedPostObject) // shows fetched data in the object form
          var fetchedPostArr = []
          fetchedPostArr = Object.entries(fetchedPostObject)
          setToDo(fetchedPostArr)
          console.log(toDo)   // []
        })
      }catch(err){
        console.log(err)
      }
    }
    fetchData()
  }, [])
}

if I don't convert the fetched object to an array then it returns an empty object if again I use the dependency, it returns an infinite state.

Nishant Kumar
  • 691
  • 1
  • 11
  • 31
  • What's the result of `Object.entries(fetchedPostObject)`? – PsyGik Jul 13 '21 at 10:39
  • @PsyGik shows the data that is stored in my database which I intend to use in my UI, something like this [[key, value], [key, value], [key, value]] where the key is the UID and value is content – Nishant Kumar Jul 13 '21 at 10:43
  • @PsyGik [Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2), Array(2)] – Nishant Kumar Jul 13 '21 at 10:45
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Yoshi Jul 13 '21 at 10:50

1 Answers1

0

You will not get the updated value just after the setState as it is async. Instead, you can try something like this

useEffect (() => {console.log(toDo)},[toDo])

This hook listens to any updated value of toDo and will execute when the value of toDo gets updated.

PsyGik
  • 3,535
  • 1
  • 27
  • 44
Akshay Mathur
  • 421
  • 4
  • 6
  • No it's not because `setState` is async (in the sense that it would be any different if it weren't), it's a simple closure issue. Ref: [useState set method not reflecting change immediately](https://stackoverflow.com/a/58877875/697154) and [How do JavaScript closures work?](https://stackoverflow.com/a/111111/697154). – Yoshi Jul 13 '21 at 10:53
  • does it given any kind of error/message in the console ? You might use a useCallback instead of useEffect and try if it helps. – Akshay Mathur Jul 13 '21 at 10:59
  • @Yoshi I went through the entire post, and there was noting that I could find to resolve the issue. I tried this setToDo((prevValues) => [...prevValues, ...fetchedPostArr]) and toDo in the 2nd arg of the useEffect which resulted into infinite loop – Nishant Kumar Jul 13 '21 at 11:06
  • @Akshay How do use callbacks instead of useEffeect hook to perform side effects in react???? – Nishant Kumar Jul 13 '21 at 11:08
  • @NishantKumar Have you understood, after reading the referenced posts, that it's impossible to `console.log` the *correct* value, at that place where you're trying to do so? – Yoshi Jul 13 '21 at 11:08
  • @Yoshi so what I'm supposed to do??? I want to use the fetched data in my UI component. Do you know any alternative way of implementation? – Nishant Kumar Jul 13 '21 at 11:11
  • The above answer results in infinite loop. – Nishant Kumar Jul 13 '21 at 11:12
  • The solution is not to expect the state being updated at that point. Calling the updater (`setToDo(fetchedPostArr)` in your case) will trigger a re-render. Only in that next render, will `toDo` have the correct value. If you simply wanted to *debug* if the correct value was returned from the api call, use `console.log(fetchedPostArr)`. Though if you wanted to react to any changes to `toDo`, regardless of where that change originated, have a separate `useEffect(() => { /* toDo has changed */ }, [toDo]);`. – Yoshi Jul 13 '21 at 11:14
  • then how do developers fetch data from firebase and show it in the UI component. How does this Facebook feed works, if it literally impossible. I'm really confused and at the point of giving up. I'm stuck on this for 4 days. I wonder how YouTubers don't face the same issue in the tutorials while using the exact same code. – Nishant Kumar Jul 13 '21 at 11:19
  • You're making your live too complicated. First make sure your api returns the correct values. Do so by `console.log`-ing that returned value, the one you will pass to your updater (`setToDo`, as I said with: `console.log(fetchedPostArr)`). If the api returns correctly, then there is nothing more for you to do. You can expect `toDo` to be updated when react decided to do so. And this update will be reflected in the next render. Meaning you can simply render your `toDo` state as you see fit. – Yoshi Jul 13 '21 at 11:27
  • I'm 200% sure that my api returns the correct values. {-MeLuxT2MftuPQlwMpkp: {…}, -MeODFeH6a-WeVyB_oVA: {…}, -MeODIv1ZiXCWbE-Wixc: {…}, -MeODLQAPCdx_788QRHj: {…}, -MeOnPccix6-nixbliEN: {…}, …} – Nishant Kumar Jul 13 '21 at 11:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/234817/discussion-between-yoshi-and-nishant-kumar). – Yoshi Jul 13 '21 at 11:37
  • @Yoshi, thank you so much. you were really very helpful in the chat. – Nishant Kumar Jul 13 '21 at 12:49