Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;

like image 857
prashant padadune Avatar asked May 15 '26 20:05

prashant padadune


1 Answers

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)
like image 155
silencedogood Avatar answered May 18 '26 10:05

silencedogood



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!