I have a useFetchPokemon
hook that includes the following code:
import { useState, useEffect } from "react";
export const useFetchPokemon = () => {
const initialUrl = "https://pokeapi.co/api/v2/pokemon"
const [pokemon, setPokemon] = useState([]) as any;
const [nextUrl, setNextUrl] = useState(null)
const [previousUrl, setPreviousUrl] = useState(null)
const getPokemonDetails = async (listOfPokemon: any) => {
const pokemonWithDetails = await Promise.all(
listOfPokemon.map(async (pokemon: any) => {
const response = await fetch(pokemon.url);
const data = await response.json();
return data;
})
);
return pokemonWithDetails;
};
useEffect(() => {
const getInitalData = async () => {
const response = await fetch(initialUrl);
const { results, next, previous } = await response.json();
const pokemon = (await getPokemonDetails(results)) as any;
setNextUrl(next);
setPreviousUrl(previous);
setPokemon(pokemon);
};
getInitalData();
}, []);
const getNextPokemon = async () => {
if(!nextUrl) return
const response = (await fetch(nextUrl)) as any
const { results, next, previous } = await response.json();
const newPokemon = await getPokemonDetails(results)
setNextUrl(next)
setPreviousUrl(previous)
setPokemon(newPokemon)
}
const getPreviousPokemon = async () => {
if(!previousUrl) return
const response = (await fetch(previousUrl)) as any
const { results, next, previous } = await response.json();
const previousPokemon = await getPokemonDetails(results)
setNextUrl(next)
setPreviousUrl(previous)
setPokemon(previousPokemon)
}
return { pokemon, nextUrl, previousUrl, getNextPokemon, getPreviousPokemon }
}
I then have a PokemonList
component that uses the hook:
import type { FC } from "react";
import { useFetchPokemon } from "../hooks/useFetchPokemon";
const PokemonList: FC = () => {
const { pokemon } = useFetchPokemon()
return (
<div>
Pokemon List
<ul>
{pokemon.map((p: any) => (
<li key={p.name}>
<img
alt={p.name}
style={{ width: "150px", height: "150px", objectFit: "cover" }}
src={p.sprites.front_default}
/>
<p>{p.name}</p>
</li>
))}
</ul>
</div>
);
};
export default PokemonList;
In my App
component I have to buttons to fetch the next set of Pokemon and the Previous set:
import PokemonList from './components/PokemonList';
import { useFetchPokemon } from './hooks/useFetchPokemon';
function App() {
const { previousUrl, nextUrl, getNextPokemon, getPreviousPokemon } = useFetchPokemon();
return (
<div className="App">
{previousUrl && <button onClick={() => getPreviousPokemon()}>Previous</button>}
{nextUrl && <button onClick={() => getNextPokemon()}>Next</button>}
<PokemonList />
</div>
);
}
export default App;
When I click on the next button, the getNextPokemon
call runs correctly and returns the correct data, but setting the new pokemon
data to the state doesn’t update the PokemonList
component. I can’t figure it out for the life of me…
2
Answers
The issue here is that you are using the useFetchPokemon hook in both the <App /> and <PokemonList /> components. This creates two separate instances of the hook, each with its own state. When you update the state in one instance of the hook, it doesn’t affect the other instance.
You could try to use your useFetchPokemon hook inside <App /> only and pass the required data to the <PokemonList /> component through props.
Plus:
Also, if you would like to achieve "shared" experience, you can use React Context:
You just need to wrap up your components like that:
So you can access shared data inside the components: