简体   繁体   中英

React - Render HTML When Setting a State After Fetching Data

I have an application which needs to fetch invoice data from Stripe API (payment processor). When the invoice data has been returned, I'm trying to update my state using this.setState({invoiceData: invoices}) where invoices is a string of HTML that I build out from the data returned from the Stripe API.

The issue is that the HTML isn't being rendered and is showing as plain text. I am pretty new to React and have only just got my head around rendering states, but now I'm pretty stuck on working this one out. What do I need to do to render the HTML? Please see my code below.

import React from 'react';

class BillingInvoices extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            invoiceData: false
        }
    }

    // When the 'BillingInvoices' component is mounted:
    componentDidMount() {

        // Get invoice data from Stripe API.
        fetch('/stripe-invoices', {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                customerId: '128973982'
            })
        })
        .then((response) => {
            if (response.ok) {
                return response.json();
            } else {
                console.log('Error with Stripe response');
            }
        })
        .then((stripeData) => {

            var invoiceCount = stripeData['result']['data'].length;
            var i;
            var invoices = '';

            for (i = 0; i < invoiceCount; i++) {
                invoices += '<div><a href="' + stripeData['result']['data'][i]['invoice_pdf'] + '" download>' + stripeData['result']['data'][i]['number'] + '</a></div>';
            }

            this.setState({
                invoiceData: invoices
            })
        })
        .catch((error) => {
            console.log('Error: ', error);
        });
    }

    render() {
        return (
            <div id="billing-invoices">
                {this.state.invoiceData ? this.state.invoiceData : null}
            </div>
        );
    }
}

export default BillingInvoices;

Thank you for any insight.

You can populate invoiceData with react components using JSX like so:

let invoices = (<div>{stripeData['result']['data'].map(data => (<div><a href={data['invoice_pdf']}  download>{data['number']}</a></div>))}</div>);

this.setState({invoiceData: invoices});

You can replace the content of the second then clause with the above and leave the rest of the code unchanged.

I've stripped out some of your code for my example to make it easier to read:

class BillingInvoices extends React.Component {

  constructor(props) {
    super(props);
    this.state = { invoiceData: [] }
  }

  componentDidMount() {
    fetch('/stripe-invoices')
      .then((response) => response.ok && response.json())

      // Here I'm assigning the nested array to `invoiceData` immediately
      // so that you don't need to map over it later
      .then((data) => this.setState({ invoiceData:  data.result.data }));
  }

  render() {

    // Here we can check if the data exists. If it doesn't
    // show a loading icon (or something) until it is
    if (!this.state.invoiceData) <Loader />

    // ...otherwise show the data
    return (
      <div id="billing-invoices">

        // we map over the invoice data and for each invoice
        // return JSX (your div with an anchor populated with that invoice data)
        {this.state.invoiceData.map((invoice) => {
          return (
            <div>
              <a href={invoice.invoice_pdf} download>{invoice.number}</a>
            </div>
          )
        })}
      );
      </div>
    )
  }
}

Putting the resulted json in the component state is a good idea.

But then, you should deal with this json directly in your render method, using the power of JSX.

Check the official documentation about how to use JSX.

This is a dummy example of what your component could look like with the usage of JSX:

import React from "react";

class BillingInvoices extends React.Component {
  constructor(props) {
    super(props);
  }

  state = {
    invoices: []
  }

  // When the 'BillingInvoices' component is mounted:
  componentDidMount() {
    // Get invoice data from Stripe API.
    fetch("/stripe-invoices", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        customerId: "128973982"
      })
    })
      .then(response => {
        if (response.ok) {
          this.setState(invoices: response.json());
        } else {
          console.log("Error with Stripe response");
        }
      })
      .catch(error => {
        console.log("Error: ", error);
      });
  }

  render() {
    return (
      <div id="billing-invoices">
        {this.state.invoices.map((invoice, index) => {
          return (
            <div key={index}>{invoice.name}</div>
          )
        })}
      </div>
    );
  }
}

export default BillingInvoices;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM