Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render async content on React?

I'm building a SPA based on WP API and want to render both posts and their featured images but they come in separate endpoints.

When rendering, React don't wait the request to resolve and get the error: "Uncaught Invariant Violation: Objects are not valid as a React child (found: [object Promise])."

Totally a beginner about Promise and Async/Await functions. Don't even know if I'm using it correctly.

import React, { Suspense, Component } from "react";
import { FontSizes, FontWeights, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import axios from "axios";
import './Home.styl'

class Home extends Component {
    constructor() {
      super();

      this.state = {
        posts: []
      }
    }

    componentWillMount() {

      this.renderPosts();

    }

    renderPosts() {

      axios.get('https://cors-anywhere.herokuapp.com/https://sextou.didiraja.net/wp-json/wp/v2/posts')
      .then((response) => {
        // console.log(response)

        this.setState({
          posts: response.data,
        })
      })
      .catch((error) => console.log(error))

    }

    async getImg(mediaId) {

      const getImg = axios
        .get('https://cors-anywhere.herokuapp.com/https://sextou.didiraja.net/wp-json/wp/v2/media/17')
        .then((response) => {
          return {
            url: response.data.source_url,
            alt: response.data.alt_text,
          }
        })

      const obj = getImg

      return (
        <img src={obj.url} />
      )

    }

    render() {

      const { posts } = this.state

      return (
        <span className="Home-route">

        <h1 style={{textAlign: 'center'}}>Sextou!</h1>

          <div className="events-wrapper">
            {
              posts.map((post, key) => {
                return (
                <div className="event-card" key={key}>

                  <img src={this.getImg()} />

                  <h2
                    className="event-title"
                    style={{ fontSize: FontSizes.size42, fontWeight: FontWeights.semibold }}
                  >
                    {post.title.rendered}
                  </h2>

                  {post.acf.event_date}

                  <span>{post.excerpt.rendered}</span>

                  <a href={post.acf.event_link} target="_blank">
                    <DefaultButton
                      text="Acesse o evento"
                    /> 
                  </a>

                  <a href={post.acf.event_ticket} target="_blank">
                    <PrimaryButton
                      text="Comprar ingressos"
                    /> 
                  </a>

                </div>

                )
              })
            } 
          </div>


        </span>
      );
    }
  }
  export default Home;
like image 330
Dico Didiraja Avatar asked Jul 25 '26 11:07

Dico Didiraja


1 Answers

You could fetch the images the same way as you fetched the posts:

Include them in your state

this.state = {
  posts: [],
  image: null
};

Call getImage in componentWillMount

componentWillMount() {
  this.getPosts();
  this.getImage();
}

setState when the promise resolves:

.then(response => {
  this.setState(state => ({
    ...state,
    image: {
      url: response.data.source_url,
      alt: response.data.alt_text
    }
  }));
});

Display a loading screen or a spinner until the image loads

render() {

  const { posts, image } = this.state;

  if (!image) {
    return "Loading";
  }

  // ...
}

I would also advise using componentDidMount instead of componentWillMount, because componentWillMount is deprecated and is considered unsafe.

Here's a codesandbox example.

like image 150
Istvan Szasz Avatar answered Jul 27 '26 04:07

Istvan Szasz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!