skip to Main Content

I have a problem, that I can’t figure out. After fetching data to an array I can display this array, but I can’t display just one object in it or map through it

useMemo(() => {
    // setTabIsLoading(true);
    console.log(tabIsLoading);
    const contentArray: Array<anime> = [];

    fetchingArray.map((animeId: string) => {
      const animeListHandler = async () => {
        try {
          const response = await fetch(
            `https://api.jikan.moe/v4/anime/${animeId}`
          );
          if (response.ok) {
            const data = await response.json();
            const transformedData = {
              id: data.data.mal_id,
              name: data.data.title,
              image: data.data.images.jpg.large_image_url,
              description: data.data.synopsis,
              score: data.data.score,
            };

            contentArray.push(transformedData);
          }
        } catch (err) {
          console.error(err);
        }
      };
      animeListHandler();
    });

    if (contentArray) {
      console.log(contentArray);
      console.log(contentArray[1]);
      const content = [];
      contentArray.map((anime) => {
        console.log(`anime: ${anime}`);
        content.push(<AnimePagesBox anime={anime} key={anime.id} />);
        return;
      });
      console.log(content);
      setActivePageContent(content);
      setTabIsLoading(false);
    }
  }, [activePage, tabIsLoading]);

that’s the output of the code (in cases of logging contentArray it shows full array with object):

true
Account.tsx:70 []
Account.tsx:71 undefined
Account.tsx:78 []
Account.tsx:41 false
Account.tsx:70 []
Account.tsx:71 undefined
Account.tsx:78 []

I also tried to add this array to an array in useState. Sometimes it worked, but still was an issue, that after getting to this page back I didn’t see the output. So I don’t even know, what to do
If I create a guard, that mapping starts only, when the length of the array is greater than 0, so it doesn’t start

2

Answers


  1. It seems like you are facing an issue where you can’t display individual objects from the contentArray.
    The fetch function is asynchronous, and you are making multiple asynchronous requests inside the fetchingArray.map loop.
    You can use Promise.all to wait for all requests to complete before processing the contentArray.

    useMemo(() => {
      const contentArray: Array<anime> = [];
    
      const fetchAnimeData = async (animeId: string) => {
        try {
          const response = await fetch(`https://api.jikan.moe/v4/anime/${animeId}`);
          if (response.ok) {
            const data = await response.json();
            const transformedData = {
              id: data.data.mal_id,
              name: data.data.title,
              image: data.data.images.jpg.large_image_url,
              description: data.data.synopsis,
              score: data.data.score,
            };
            contentArray.push(transformedData);
          }
        } catch (err) {
          console.error(err);
        }
      };
    
      const promises = fetchingArray?.map((animeId: string) => fetchAnimeData(animeId));
    
      Promise.all(promises).then(() => {
        const content = contentArray?.map((anime) => {
          console.log(`anime: ${anime}`);
          return <AnimePagesBox anime={anime} key={anime.id} />;
        });
        console.log(content);
        setActivePageContent(content);
        setTabIsLoading(false);
      });
    }, [activePage, tabIsLoading]);
    
    Login or Signup to reply.
  2. As others have suggested, use Promise.all. It will return a promise containing the results of every promise passed into it. Also, you should not pass the entire anime object into the subcomponent. Try to pass primitive values if you can. If you look at the <AnimeBox> component below, you will see how props can be spread into the component.

    Here is a runnable Stack Snippet demonstrating this:

    Note: I switch from async/await to Promise.then in the snippet below, because the snippet’s Babel version does not support the former.

    const { useEffect, useState } = React;
    
    const fetchAnimeData = (animeId) =>
      fetch(`https://api.jikan.moe/v4/anime/${animeId}`)
        .then(response => response.json())
        .then((payload) => payload.data ? ({
            id: payload.data.mal_id,
            name: payload.data.title,
            image: payload.data.images.jpg.large_image_url,
            description: payload.data.synopsis,
            score: payload.data.score,
          }) : null);
    
    const fetchMultiAnimeData = (...animeIds) =>
      Promise.all(animeIds.map(fetchAnimeData));
    
    const AnimeBox = ({ id, name, image, description, score }) => {
      return (
        <div className="AnimeBox">
          <h2>{name} ({score}/10)</h2>
          <img height="128" src={image} />
          <p>{description}</p>
        </div>
      );
    };
    
    const App = () => {
      const [animeDataList, setAnimeDataList] = useState([]);
      
      useEffect(() => {
        // ID 7 is invalid, and will not break rendering...
        fetchMultiAnimeData(1, 5, 6, 7).then(res => {
          // Filter out any null results
          setAnimeDataList(res.filter(v => v && v.id));
        });
      }, []);
    
      return (
        <div className="App">
          <React.Fragment>
            {animeDataList.map(data => (
              <AnimeBox key={data.id} {...data} />
            ))}
          </React.Fragment>
        </div>
      );
    };
    
    ReactDOM
      .createRoot(document.getElementById("root"))
      .render(<App />);
    html, body, #root {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
    
    #root {
      display: flex;
      flex-direction: column;
    }
    
    .App {
      display: flex;
      gap: 1rem;
      padding: 1rem;
    }
    
    .AnimeBox {
      display: flex;
      align-items: center;
      justify-content: flex-start;
      flex-direction: column;
      border: thin solid grey;
      flex: 1;
      padding: 0.25rem;
    }
    
    .AnimeBox h2 {
      text-align: center;
    }
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search