0

I want to create a function that maps throw a firestore collection and return a Component for each document in the collection I have tried to use the code below

  <div className="posts">
          
                {
                  db.collection("posts")
                  .get()
                  .then((snapshot) => {
                    snapshot.docs.map((doc)=>(
                      <PostContect img={doc.data().image}   Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title}  />
                 
                  ))})}
                
</div>

but this error shows:

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.ode

norbitrial
  • 14,716
  • 7
  • 32
  • 59
Mohamad Amjad
  • 19
  • 1
  • 9
  • Does this answer your question? [Reactjs async rendering of components](https://stackoverflow.com/questions/27192621/reactjs-async-rendering-of-components) – Victor Peralta Jan 28 '21 at 18:22

4 Answers4

1

Since you haven't given a hint as to whether you're using hooks or not, here is how you should deal with asynchronous stuff and setting states in React (using hooks) :-

function MyComponent () {
const [snapshot,setSnapshot] = useState();

useEffect(()=>{
async function getPosts(){
    let snapshot = await db.collection("posts").get();
    setSnapshot(snapshot);
}
getPosts();
},[])

return 
(<div className="posts">
      snapshot?.docs?.map((doc)=>(
       <PostContect img={doc.data().image}   Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title}  />)
</div>)
}

What you are doing is returning Promise object as the error states. That async stuff is taken care either inside a useEffect,event handler or a custom-hook. There could be more ways but this is a general point to start.

Lakshya Thakur
  • 8,030
  • 1
  • 12
  • 39
1

As the error message states Promise is not a valid React child. To solve that issue you can introduce a state which keeps the result of db.collection('posts').get() method. Then iterating through on that array with your .map() which will be a valid React child.

See a possible solution for your scenario:

// in the root of you component, introduce a state
const [posts, setPosts] = useState([])

// here in useEffect you can do your API call and update the state
useEffect(() => {
   db.collection("posts")
     .get()
     .then((snapshot) => {
        setPosts(snapshot.docs)
     })
}, [])

// ... rest of your component's code

return (
  <div className="posts">
     {
         posts && posts.map((doc, i) => (
            <div key={i}>
                 doc.data().title
            </div>
            // here you can use <PostContect /> component
            // either doc.data().title or doc.title to get the value
         ))
     }
  </div>
)

Suggested reads:

norbitrial
  • 14,716
  • 7
  • 32
  • 59
1

JSX is not the place to make Ajax calls. Instead make the call inside componentDidMount/useEffect and set the data received inside state. As soon as state is updated with received data, React will automatically re-render the component and display the expected content. Try something as follows:

const [snapshots, setSnapshots] = useState();
useEffect(()=> {
    db.collection("posts")
     .get()
     .then((snapshot) => {
      setSnapshots(snapshot.docs)            
     }
    )
}, []);

render
<div className="posts">
    {snapshots && snapshots.map((doc)=>(
      <PostContect img={doc.data().image} Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title}  />
     )
    }            
</div>
Peter
  • 10,492
  • 21
  • 82
  • 132
0

The code you wrote returns a promise, which can't be rendered by react. You can use conditional rendering to return something else while the data is being fetched.

Check this question for more info