49
votes

I am trying to render a list of posts by mapping through an array. I've done this many times before but for some

renderPosts = async () => {
    try {
      let res = await axios.get('/posts');
      let posts = res.data;
      return  posts.map((post, i) => {
        return (
          <li key={i} className="list-group-item">{post.text}</li>
        );
      });
    } catch (err) {
      console.log(err);
    }
  }

  render () {
    return (
      <div>
        <ul className="list-group list-group-flush">
          {this.renderPosts()}
        </ul>
      </div>
    );
  }

All I get is:

Uncaught 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.

I've checked the data returned from renderPosts and it is an array with the correct values and no promises. What's going on here?

4
You can only ever return 1 object from react. You need to change return posts.map to const someVar = posts.map, and then return <ul>{someVar}</ul> ... that way, you're returning a wrapped object. reactjs.org/docs/lists-and-keys.html - Aaron
while the first comment is true, the larger problem is that you're trying to return JSX from an async method which won't work. you need to fetch your async data in componentDidMount() and call this.setState when your api returns instead of returning JSX directly - azium
I tried this before with componentWillMount because I assumed that was the issue but it didn't work. Just did it with componentDidMount and it worked! Thanks azium. - Aaron

4 Answers

38
votes

this.renderPosts() will return a Promise not the actual data, and AFAIK Reactjs will not resolve Promises implicitly in render.

You need to do it like this

componentDidMount() {
  this.renderPosts();
}

renderPosts = async() => {
  try {
    const res = await axios.get('/posts');
    const posts = res.data;

    // this will re render the view with new data
    this.setState({
      Posts: posts
    });
  } catch (err) {
    console.log(err);
  }
}

render() {
  const posts = this.state.Posts?.map((post, i) => (
    <li key={i} className="list-group-item">{post.text}</li>
  ));

  return (
    <div>
      <ul className="list-group list-group-flush">
        {posts}
      </ul>
    </div>
  );
}
30
votes

I also received the same error message when creating an async functional component. Functional components should not be async.

const HelloApp = async (props) =>  { //<<== removing async here fixed the issue
  return (
    <div>
      <h2>Hello World</h2>
    </div>
  )
}
ReactDOM.render(<HelloApp />, document.querySelector("#app"))

jsfiddle

0
votes

Poor me

For anyone using jest test

And trying to call a function children then received this Error

please check:

const children = jest.fn().mockReturnValueOnce(null)

NOT

const children = jest.fn().mockRejectedValue(null);
-2
votes

Using React Hooks:

import React, {useState, useEffect} from "react"

const ShowPosts = () => {
    const [posts, setPosts] = useState([]);
    
     useEffect( async () => {
         try {
            const res = await axios.get('/posts');
            setPosts(res.data);
         } catch (err) {
            console.log(err);
         }
     }, []); 
    
     return <div>{posts}</div>
}