简体   繁体   中英

Wait for API data before render react hooks

I make two API call. Data from getAllCampaign api renders but for loadStats It appears React goes ahead to build a table without the data, thus throwing error of Uncaught TypeError: Cannot read property 'data' of undefined Here's what I'm doing.

coreapihelpers:

export const getAllCampaign = () => {
    return fetch(`https://api.truepush.com/api/v1/listCampaign/1`, {
        method: "GET",
        headers: {
            Authorization: `$TOKEN`,
            "Content-Type": "application/json",
        },
    })
    .then(response => {
        return response.json()
    })
    .catch(err => console.log(err))
}

export const loadStats = async () => {
    const ids = await getAllCampaign()

    const data = Promise.all(
        ids.data.map(async (i) => await (await fetch(`https://api.truepush.com/api/v1/campaignStats/${i.campaignId}`, {
            method: "GET",
            headers: {
                Authorization: ``$TOKEN``,
                "Content-Type": "application/json"
            }
        })).json())
    )
    return data
};

allData output is delayed on console

Full code:

import React, {useState, useEffect} from 'react';
import {getAllCampaign, loadStats} from "../helpers/coreapihelpers";

const TableRow = () => {

    const [campaigns, setCampaigns] = useState([]);
    const [stats, setStats] = useState([]);

    const loadAllCampaigns = () => {
        getAllCampaign()
            .then(data => { setCampaigns(data.data) })
            .catch(err => { console.log(err) });
    };

    const loadAllStats = () => {
        loadStats()
            .then(data => { setStats(data) })
            .catch(err => { console.log(err) });
    }

    useEffect(() => {
       loadAllCampaigns();
       loadAllStats();
    }, [])

    const allData = campaigns.map ? campaigns.map((campaign, i) => ({
        ...campaign,
        ...stats[i],
    }))
        : <h1>API LIMIT EXCEEDS</h1>

    return (
        <div className="container">
            <div className="row">
                <div className="col-xs-12">
                    {allData.map ? allData.map((campaigns, index) => (
                        <div className="table-responsive" data-pattern="priority-columns">
                            <table className="table table-bordered table-hover">
                                <thead>
                                <tr>
                                    <th>Sr. No</th>
                                    <th>Campaign Id</th>
                                    <th>Campaign Name</th>
                                    <th>Campaign Status</th>
                                    <th>Reach</th>
                                    <th>Sent</th>
                                    <th>Delivered</th>
                                    <th>Views</th>
                                    <th>Clicks</th>
                                    <th>Unsubscribers</th>
                                </tr>
                                </thead>
                                <tbody>
                                <tr key={index}>
                                    <td>{index + 1}</td>
                                    <td>{campaigns.campaignId}</td>
                                    <td>{campaigns.campaignTitle}</td>
                                    <td>{campaigns.campaignStatus}</td>
                                    <td>{campaigns.data.Reach}</td>
                                    <td>{campaigns.data.Sent}</td>
                                    <td>{campaigns.data.Delivered}</td>
                                    <td>{campaigns.data.Views}</td>
                                    <td>{campaigns.data.Clicks}</td>
                                    <td>{campaigns.data.Unsubscribers}</td>
                                </tr>
                                </tbody>
                            </table>
                        </div>)) : <h1>API Limit Exceeds / API Token Broken</h1>}
                    </div>
                 </div>
            </div>
            );
        }

export default TableRow;

I think the problem is that you should be checking whether state campaigns has been populated, not allData . allData is a variable, not the actual state. Actual state should be used for conditional rendering. Try this:

Replace this:

allData.map ? allData.map((campaigns, index)

With this:

campaigns ? allData.map((campaigns, index)

However, you should create an object for state that contains both campaigns and stats, since you intend to use them as corresponding items. Or, your database query should return them as one item with some time of join.

Try this for a client side solution:

const [stateObj, setObj] = useState([]);
const campaigns = [];
const stats = [];

const loadAllState = async () => {
    try {
       campaigns = await getAllCampaign();
    } catch (err) {
       console.log(err)
    }

    try {
       stats = await loadStats();
    } catch (err) {
       console.log(err)
    }

    setObj(campaigns.map((campaign, i) => ({
       ...campaign,
       ...stats[i],
  })))
};

useEffect(() => {
   loadAllState();
}, [])

Then:

stateObj ? stateObj.map((campaigns, index)

You should see for allData if it is defined or not like this

 {allData ? allData.map((campaigns, index) => .......}

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