简体   繁体   中英

how can i render a component with an API call with props react js

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;

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" />

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM