How to fetch and display data from multiple endpoints in React?

Viewed 574

I have two endpoints. I want to fetch data from them at once and display in one element, for example display data from one table and name from another if the product id is the same. This is how I do this now for one endpoint:

import React, {useState,useEffect} from 'react'
import products from '../apis/apiURLS'
import './Products.css';

const Products = () => {
    const [productInfo,setProductInfo] = useState([]);

    const fetchProducts  = async () =>{
        const response = await products.get('/products/')
        setProductInfo(response.data)
    }

    useEffect(()=>{
        fetchProducts()
    },[])

    return (
      <div>
      <h1 className = "header">Products</h1>      
      {productInfo.map(products=> {
        return (
          

      <div className="productInfo">
        {console.log(products)}
        <p>Date: {products.date}</p>

      </div>
          
      )

        
})}
</div>
    )}

export default Products

EDIT
Products endpoint has those fields: date, product_id and Stock endpoint has fields: product_id, name and product_id is the foreign key to Stock.
I was trying to add another async function for second endpoint:

const [stockInfo, setStockInfo] = useState([]);
const fetchStock  = async () =>{
      const response = await stock.get('/stock/')
      setStockInfo(response.data)
  }
    useEffect(()=>{
      fetchStock()
  },[])

But I can't map it correctly to display in one element. What I want to achieve is:

<p>Product: {stock.name}, {products.date}</p>
1 Answers

If you consider that the result of requesting to both APIs are dependants, you can use Promise.all to ensure that the fetch completes successfully for both endpoints.

Thereafter you can merge the results by using the product_id field and a single useState hook to set the combined data in the component state.

For example,

const combineUsingProductIdField = (products, stocks) => {
  const keyByProductIdField = (previousValue, currentValue) => ({
    ...previousValue,
    [currentValue.product_id]: currentValue,
  });
  const stocksByProductId = stocks.reduce(keyByProductIdField, {});

  const productsWithStock = products.map(product => ({
    product,
    stock: stocksByProductId[item.product_id],
  }));
  return productsWithStock;
};

const Products = () => {
  const [productsWithStock, setProductsWithStock] = useState([]);

  const fetchProductsWithStock = () => {
    const fetchProducts = async () => {
      return await products.get('/products/');
    };
    const fetchStocks = async () => {
      return await stock.get('/stock/');
    };

    try {
      const [_products, _stocks] = await Promise.all([
        fetchProducts,
        fetchStocks,
      ]);
      setProductsWithStock(combineUsingProductIdField(products, stocks));
    } catch (err) {
      // log to error reporting;
    }
  };

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

  return (
    <div>
      <h1 className="header">Products</h1>
      {productsWithStock.map(({ product, stock }) => {
        return (
          <div className="productInfo">
            <p>
              Product: {stock.name}, {product.date}
            </p>
          </div>
        );
      })}
    </div>
  );
};

export default Products;

I recommend having a single API endpoint that returns this combined data if you can. That way you would be relying on already existing relationship defined in your data models to pull the stock data for each product.

Related