Home > Software engineering >  How to use data of an Async function inside a functional component to render HTML in React
How to use data of an Async function inside a functional component to render HTML in React

Time:05-03

I've been trying to use the data I get from an Async function inside of another function I use to display HTML on a react project. I have made several attempts but nothing seems to work for me. Hope any of you could help me. Please correct me if I did anything wrong.

I've tried it with a useEffect as well:

import React, { useState, useEffect } from 'react';
import { getGenres } from './api/functions';

const ParentThatFetches = () => {
  const [data, updateData] = useState();
  useEffect(() => {
    const getData = async () => {
      const genres = await getGenres('tv');
      updateData(genres);
    }
    getData();
  }, []);

  return data && <Screen data={data} />
}

const Screen = ({data}) => {
  console.log({data}); //logs 'data: undefined' to the console
  return (
    <div>
      <h1 className="text-3xl font-bold underline">H1</h1>
    </div>
  );
}


export default Screen;

The Error I get from this is: {data: undefined}.

The getGenres function that makes the HTTP Request:

const apiKey = 'key';
const baseUrl = 'https://api.themoviedb.org/3';

export const getGenres = async (type) => {
    const requestEndpoint = `/genre/${type}/list`;
    const requestParams = `?api_key=${apiKey}`;
    const urlToFetch = baseUrl   requestEndpoint   requestParams;
    try {
        const response = await fetch(urlToFetch);
        if(response.ok) {
            const jsonResponse = await response.json();
            const genres = jsonResponse.genres;
            return genres;
        }
    } catch(e) {
        console.log(e);
    }
}

I want to use the data inside my HTML, so the H1 for example. Once again, haven't been doing this for a long time so correct me if I'm wrong.

CodePudding user response:

There are a few conceptual misunderstandings that I want to tackle in your code.

In modern React, your components should typically render some type of jsx, which is how React renders html. In your first example, you are using App to return your genres to your Screen component, which you don't need to do.

If your goal is to fetch some genres and then ultimately print them out onto the screen, you only need one component. Inside that component, you will useEffect to call an asynchronous function that will then await the api data and set it to a react state. That state will then be what you can iterate through.

When genres is first rendered by react on line 6, it will be undefined. Then, once the api data is retrieved, React will update the value of genre to be your array of genres which will cause the component to be re-rendered.

{genres && genres.map((genre) ... on line 20 checks to see if genres is defined, and only if it is, will it map (like looping) through the genres. At first, since genres is undefined, nothing will print and no errors will be thrown. After the genres are set in our useEffect hook, genres will now be an array and we can therefore loop through them.

Here is a working example of your code.

import React, { useState, useEffect } from "react";

import { getGenres } from "./api/functions";

function App() {
  const [genres, setGenres] = useState();

  useEffect(() => {
    async function apiCall() {
      const apiResponse = await getGenres("tv");
      console.log(apiResponse);
      setGenres(apiResponse);
    }
    apiCall();
  }, []);

  return (
    <div>
      <h1 className="text-3xl font-bold underline">H1</h1>
      {genres && genres.map((genre) => <div key={genre}>{genre}</div>)}
    </div>
  );
}

export default App;

CodePudding user response:

You should use a combination or useEffect and useState

You should use useEffect to launch the async get, without launching it each rerendering. If a async function is used to get some data from outside the component, this is called 'side effect' so use useEffect.

You should use useState to react to changes on theses side effects, re-rendering the component to get the data in the dom.

In the next example, Im using a dummy async function getGenres which returns an array of genres.

Here is an example and a WORKING EXAMPLE :

const {useState, useEffect} = React;


async function getGenres() {

    var promise = new Promise(function(resolve, reject) {
     window.setTimeout(function() {
       resolve( ['genre1', 'genre2']);
     });
   });
   return promise;
   
}

const Screen = () => {

    const [genres, setGenres] = useState([])


  useEffect(
    async () => {
        const newGenres = await getGenres();
      setGenres(newGenres)
    }, [getGenres]
  )
    
  return (
    <ul>
      {
      genres.map(
        i => <li key={i}>{i}</li>
      )
      }
    </ul>
  );
}


const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<Screen/>)
  • Related