skip to Main Content

I’m currently using an NFL Api to create an analytics website. I’m trying to map through 3 separate arrays in my react component. One of the arrays is holding names of players I retrieved from the API fetchFullNames. Another array is holding hrefs of the player’s headshots fetchImages. And then one array’s holding the id for each player quarterbacks. In my return statement is where I am trying to map through each of these arrays displaying 5 cards, one for each player containing their name and headshot. My code right now is displaying the same player’s name multiple times and is also displaying the same player’s headshot multiple times. I’m wondering if I should be using just one map function for all the data in my return statement.

This is the code I’m currently working with:

import { Link } from "react-router-dom";
import _navbar from "../../NavBar/navbar";
import React, { useEffect, useState } from "react";
import styles from "./card.module.css";

const quarterbacks = [3139477, 4241479, 3918298, 3915511, 2577417];
  const fetchFullName = async (id) => {
    const res = await fetch(
      `https://nfl-api-data.p.rapidapi.com/nfl-ath-fullinfo?id=${encodeURIComponent(id)}`,
      {
        headers: {
          "x-rapidapi-key": "secret key",
          "x-rapidapi-host": "nfl-api-data.p.rapidapi.com",
        },
      },
    );
    if (!res.ok) {
      throw new Error(`Name lookup for id '${id}' failed`);
    }
    return (await res.json()).athlete.fullName;

  };
  
  // returns an array of {id, name} objects
  const fetchFullNames = async (ids) =>
    Promise.all(ids.map(async (id) => ({ id, name: await fetchFullName(id) })));


  const fetchImage = async (id) => {
    const res = await fetch(
      `https://nfl-api-data.p.rapidapi.com/nfl-ath-img?id=${encodeURIComponent(id)}`,
        {
          headers: {
            "x-rapidapi-key": "secret key",
            "x-rapidapi-host": "nfl-api-data.p.rapidapi.com",
          },
        },
      );
      if(!res.ok) {
        throw new Error(`Image lookup for id '${id}' failed`);
        
      }
      return (await res.json()).image.href;
    
  }
  // Returns an array of the href for each player 
  const fetchImages = (ids) => 
    Promise.all(ids.map(async (id) => ({image : await fetchImage(id)})));
  

  
  

  export default function _quarterbacksPage() {
    const [names, setNames] = useState([]);
    const [images, setImages] = useState([]);
  
    useEffect(() => {
      fetchFullNames(quarterbacks).then(setNames).catch(console.error);
      fetchImages(quarterbacks).then(setImages).catch(console.error);
    }, []);
  
    return (
      <>
        <_navbar />
        <div className={styles.cards}>
          {quarterbacks.map(() => (
            <div className={styles.card}>   
              {images.map(({image}) => (             
                <img src={image} key={image} alt="player picture"/>
              ))}
              {names.map(({id, name }) => (
                <Link className={styles.cardText} key={id} to={`/quarterback/${id}`}>
                  {name}
                </Link>
              ))}
            </div>
          ))}
        </div>
          
      </>
    );
  }

2

Answers


  1. It might be easier to store all the qb data in one state variable (as an array of objects). Then you can just display each field appropriately while mapping once over qbData.

    const [qbData, setQbData] = useState([]);
    
    useEffect(() => {
      Promise.all([
        fetchFullNames(quarterbacks),
        fetchImages(quarterbacks)
      ])
      .then(([names, images]) => setQbData(
        quarterbacks.map((id, i) => ({ 
          id,
          name: names[i], 
          image: images[i]
        }))
      ))
    }, []);
    
    
    
    <div className={styles.cards}>
          {qbData.map(({id, name, image})) => (
            <div className={styles.card}>              
                <img src={image} key={image} alt="player picture"/>
                <Link className={styles.cardText} key={id} to={`/quarterback/${id}`}>
                  {name}
                </Link>
            </div>
          ))}
        </div>
    
    Login or Signup to reply.
  2. You can focus on each ID in isolation and keep your code DRY:

    const RAPIDAPI_KEY = "secret key";
    
    async function fetchJsonData(endpoint, params) {
      const init = {
        headers: new Headers([
          ["x-rapidapi-key", RAPIDAPI_KEY],
          ["x-rapidapi-host", "nfl-api-data.p.rapidapi.com"],
        ]),
      };
    
      const url = new URL("https://nfl-api-data.p.rapidapi.com");
      url.pathname = endpoint;
      if (params) url.search = new URLSearchParams(params).toString();
    
      const response = await fetch(url, init);
    
      if (!response.ok) {
        throw new Error(`Response not OK (${response.status})`, {
          cause: response,
        });
      }
    
      return response.json();
    }
    
    async function fetchFullName(id) {
      try {
        const data = await fetchJsonData("/nfl-ath-fullinfo", { id });
        return data.athlete.fullName;
      } catch {
        throw new Error(`Name lookup for id '${id}' failed`);
      }
    }
    
    async function fetchImage(id) {
      try {
        const data = await fetchJsonData("/nfl-ath-img", { id });
        return data.image.href;
      } catch {
        throw new Error(`Image lookup for id '${id}' failed`);
      }
    }
    
    async function fetchQuarterbackData(id) {
      const [name, imageUrl] = await Promise.all([
        fetchFullName(id),
        fetchImage(id),
      ]);
      return { id, name, imageUrl };
    }
    

    Then in the component, you only need to map once:

    export default function _quarterbacksPage() {
      const [quarterbacks, setQuarterbacks] = useState([]);
    
      useEffect(() => {
        (async () => {
          try {
            const quarterbackIds = [3139477, 4241479, 3918298, 3915511, 2577417];
            const qbs = await Promise.all(quarterbackIds.map(fetchQuarterbackData));
            setQuarterbacks(qbs);
          } catch (cause) {
            console.error(cause);
          }
        })();
      }, []);
    
      return (
        <>
          <_navbar />
          <div className={styles.cards}>
            {quarterbacks.map(({ id, name, imageUrl }) => (
              <div key={id} className={styles.card}>
                <img src={imageUrl} alt="player picture" />
                <Link className={styles.cardText} to={`/quarterback/${id}`}>
                  {name}
                </Link>
              </div>
            ))}
          </div>
        </>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search