0

I am trying to render a component that call an api with the prop that i am passing, but i have this error: Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

And other that say that it cannot read the property map of null, this is my code

import React, { useEffect } from "react";

const GetLeagues = async (country) => {
    const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
    const res = await fetch(url);
    const { countrys } = await res.json();

    return (
        <div>
            <ul>
                {countrys.map((country, i) => {
                    return <li key={i}>{country.strLeague}</li>;
                })}
            </ul>
        </div>
    );
};

const Leagues = () => {
    useEffect(() => {
        GetLeagues();
    }, []);

    return (
        <div>
            <GetLeagues country={"Spain"} />
        </div>
    );
};

export default Leagues;

1
  • A function component can never by an async function because when rendering the rendering engine wants to know what to render at the moment of calling. An async function always returns a promise and promises cannot be rendered, since the value is not available until the promise is resolved. Commented Nov 2, 2020 at 0:41

3 Answers 3

1

You shouldn't make side effects in the component body, you should use useEffect to make side effects, hence you shouldn't make your components async but instead, you can define your functions as async, use them in useEffect and then set your state.

function Leagues({ country }) {
  const [countryData, setCountryData] = React.useState([]);

  React.useEffect(() => {
    async function getCountries() {
      const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
      const res = await fetch(url);
      const { countrys } = await res.json();
      setCountryData(countrys);
    }

    getCountries();
  }, [country]);

  return (
    <div>
      <ul>
        {countryData.map((country, i) => {
          return <li key={i}>{country.strLeague}</li>;
        })}
      </ul>
    </div>
  );
}

ReactDOM.render(
  <Leagues country="Spain"  />,
  document.getelementById("root")
);

Since async functions is not supported in the snippet, here is a working version with .then promise chaining.

function Leagues({ country }) {
  const [countryData, setCountryData] = React.useState([]);

  React.useEffect(() => {
    const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${country}&s=Soccer`;
    fetch(url)
      .then((res) => res.json())
      .then(({ countrys }) => setCountryData(countrys));
  }, [country]);

  return (
    <div>
      <ul>
        {countryData.map((country, i) => {
          return <li key={i}>{country.strLeague}</li>;
        })}
      </ul>
    </div>
  );
}

ReactDOM.render(
  <Leagues country="Spain" />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root" />

Sign up to request clarification or add additional context in comments.

Comments

0

Here is the component, just call it in App with <Leagues c='Spain'/> I call the parameter c because country was clearly not readable having countrys everywhere.

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


const Leagues = ({c}) => {
    const [countrys, countrysSet] = useState(false);

    useEffect(() => {
        countrysSet(false);
        const url = `https://www.thesportsdb.com/api/v1/json/1/search_all_leagues.php?c=${c}&s=Soccer`;
        fetch(url).then( res => res.json()).then(countrysSet);
    }, [c]);

    if( countrys === false ) {
        return <p>loading...</p>;
    }

    return (
        <div>
            <ul>
                {countrys.map((country, i) => {
                    return <li key={i}>{country.strLeague}</li>;
                })}
            </ul>
        </div>
    );
};

export default Leagues;

2 Comments

should not the async/await code inside the useEffect be written in another async function? and call that function inside useEffect callback.
I edited, now there is no more async. Is it more clear?
0

I think you should break that code down in a few parts. Also, you cannot make api calls in the body of your function, else it will run every time your component is re rendered. Let me show you using your example:

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

   const GetLeagues = async (country) => {
       // This helper function fetches your leagues
       const url = 'your url'
       const res = await fetch(url);
       const { countries } = await res.json();
   
       return countries;
   };
   
   const Leagues = () => {
       const [leagues, setLeagues] = useState([]);

       useEffect(() => {
          async function init() {
          // Declaring an extra function as useEffect
          // cannot be async.
           const countries = await GetLeagues();
            setLeagues(countries);
           }
           init();
       }, []);
   
       return (
           <div>
                {leagues.map((country, i) => {
                   return <li key={i}>{country.strLeague}</li>;
               })}
           </div>
       );
   };

   export default Leagues;

Note that now "GetLeagues" is just an utilitary function, not a React component. So that could be reused without rendering anything.

Also, your "Leagues" component handles all the necessary operations to render itself.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.