I'm trying to create a component that allows me to access API data anywhere I import it.
I have a GetTeamsByLeague
component which looks like this.
import axios from 'axios';
const GetTeamsByLeague = (league: string) => {
const teams = axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
return response;
})
.catch((error: any) => {
console.log(error);
});
return teams;
};
export default GetTeamsByLeague;
Then in my app component, I have this
import Header from './Components/Header/Header';
import GetTeamsByLeague from './Hooks/GetTeamsByLeague';
import './Reset.scss';
function App() {
const championshipTeams = GetTeamsByLeague('Championship');
console.log(championshipTeams);
return (
<div className='App'>
<Header/>
</div>
);
}
export default App;
The issue is the console.log()
just return the promise not the data.
If I use useState()
in my GetTeamsByLeague component like this
import axios from 'axios';
import { useState } from 'react';
const GetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState({});
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
return teams;
};
export default GetTeamsByLeague;
Then I get the following errors
(3) [{…}, {…}, {…}] ...
GET http://awaydays-api.cb/api/teams/Championship 429 (Too Many Requests)
/* EDIT */
I've now updated my GetTeamsByLeague component too
import axios from 'axios';
import { useEffect, useState } from 'react';
interface Teams {
id: number;
name: string;
league: string;
created_at: string;
updated_at: string;
}
const useGetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState<Teams[]>();
useEffect(() => {
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
}, [league, setTeams]);
return teams;
};
export default useGetTeamsByLeague;
In my component done
import useGetTeamsByLeague from './Hooks/useGetTeamsByLeague';
import './Reset.scss';
function App() {
const teams = useGetTeamsByLeague('Championship');
console.log(useGetTeamsByLeague);
return (
<div className='App'>
<Header>
{teams.map(team => {
<li>{team.name}</li>;
})}
</Header>
</div>
);
}
export default App;
But now I get TypeScript error Object is possibly 'undefined'
The console.log shows empty array first then the data
undefined
(3) [{…}, {…}, {…}]
CodePudding user response:
In the first case, you return a Promise since you don't await the axios fetch. In the second case, after the axios fetch succeeds, you store the result in the state which re-renders the component, which causes an infinite loop of fetching -> setting state -> re-rendering -> fetching [...].
This is a perfect use case for a React hook. You could do something like this:
const useGetTeamsByLanguage = (language) => {
const [status, setStatus] = useState('idle');
const [teams, setTeams] = useState([]);
useEffect(() => {
if (!language) return;
const fetchData = async () => {
setStatus('fetching');
const {data} = await axios.get(
`http://awaydays-api.cb/api/teams/${league}`);
if(data){
setTeams(data);
}
setStatus('fetched');
};
fetchData();
}, [language]);
return { status, teams };
};
And then inside your components do
const {status, teams} = useGetTeamsByLanguage("someLanguage");
Ofc. you should modify the hook to your needs since I don't know how your data structure etc. looks like.
CodePudding user response:
A component function is run on every render cycle, therefore the request is many times. You should use the useEffect()
hook (documentation).
Wrapping this logic in a component is probably not the right tool to use in this case. You should probably consider a custom hook instead, for example:
const useGetTeamsByLeague = (league: string) => {
const [teams, setTeams] = useState({});
useEffect(() => {
axios
.get(`http://awaydays-api.cb/api/teams/${league}`)
.then((response: any) => {
setTeams(response.data);
})
.catch((error: any) => {
console.log(error);
});
}, [league, setTeams]);
return teams;
};