1

Logging the results of the firestore query yields all the data I needed, however using setPosts to store it in the "posts" variable returns undefined. I'm sure I'm missing something obvious...

const Posts = () => {
      const [posts, setPosts] = useState();
    
      
      useEffect(() => {
        db.collection('posts')
        .get()
        .then((data) => {
          const results = data.docs.map((doc) => doc.data())
          console.log(results);
          // (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
          setPosts(results);
          console.log(posts);
          // undefined - why? 
        })
        .catch((error) => {
          console.error(error);
        });
      }, []);
    
      return ()};
ikoza
  • 93
  • 1
  • 9
  • 3
    setState call is asynchronous, so the state might be undefined or incorrect if you try to access it right after setting, and its better to set initial state as empty array ```useState([])``` in your case since you try to set it with array of objects – Mod3rnx Mar 13 '21 at 12:28
  • 1
    https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately – Nadia Chibrikova Mar 13 '21 at 12:32
  • 4
    Does this answer your question? [Why calling react setState method doesn't mutate the state immediately?](https://stackoverflow.com/questions/30782948/why-calling-react-setstate-method-doesnt-mutate-the-state-immediately) – Ajeet Shah Mar 13 '21 at 12:33
  • I've set the empty array in useState, thanks! looks nicer, alright. So how do I go about the setState, not to get the empty array on page load? – ikoza Mar 13 '21 at 12:35
  • you can move console.log(posts) out of all hooks, and put it right inside Post component somewhere between useEffect and return and see what it logs. Should log empty on first load and then as useEffect fires, log the data if everything else is correct in useEffect where u get the data. – Mod3rnx Mar 13 '21 at 12:37
  • Try to render some jsx that depends on the posts data. You'll see it is working. – Dilshan Mar 13 '21 at 12:51
  • @Dilshan: logging out the data is for checking purposes. I wanted to render the posts in the return of the component, but it's not there yet on first render. Should I edit the question to include the full component with the reder part? – ikoza Mar 13 '21 at 13:07
  • 2
    If you need the data on first render then, do server side rendering. But I assume you don't have a requirement to add SSR to the application. Then the way you need to handle is only render your jsx if posts are available. Otherwise just show some loading message or a splash screen https://reactjs.org/docs/conditional-rendering.html – Dilshan Mar 13 '21 at 13:11

2 Answers2

6

When you are doing console.log(posts) in your useEffect(), posts here is referencing to the value you are defining for the very first render, which is undefined because you didn't specify a default value in useState().

If you put console.log(posts) outside of useEffect(), you will see that it will log posts twice, once for the first render which will still logs out undefined, and another one on the second render triggered after you do setPosts().

Think of it like this, React updates your UI by rendering every time a state is updated. Effects, are tied to a specific render. In your original code, your component is rendering 2 times.

  1. The initial render, which will run the effect as well.
  2. The second render, triggered after setPosts(results) updated the state. No effects are run here, because you put [] as the dependency of the useEffect() hook, causing it to only run once (when the component mounted).

You are doing console.log(posts) inside the effect, which means it is part of the initial render. Think about it for a second, what is the value of posts in the initial render? It is still undefined! The new value for posts is only available in the second render.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Jackyef
  • 4,734
  • 18
  • 26
0

Removed the console.log-s and now it's all working. No idea why... should I completely delete the question?

The working code for comparison:

const Posts = () => {
  const [posts, setPosts] = useState([]);

  
  useEffect(() => {
    db.collection('posts')
    .get()
    .then((data) => {
      const results = data.docs.map((doc) => doc.data())
      setPosts(results);
    })
    .catch((error) => {
      console.error(error);
    });
  }, []);
  
  return ()};
ikoza
  • 93
  • 1
  • 9