I have this pokemon api app, and it haves some issues, firstly when i fetch pokemon, it keeps adding infinite x pokemon with 1 request. Secondly, when I try to type something again, the app literally freezes. Code below:
App:
import React from 'react'
import './App.css'
import {PokemonForm} from '@components/PokemonForm/PokemonForm'
import {PokemonProvider} from "@context/PokemonContext";
import {PokemonList} from "@components/PokemonForm/PokemonList";
const App: React.FC = () => {
return (
<PokemonProvider>
<PokemonList/>
<PokemonForm/>
</PokemonProvider>
)
}
export default App
Form: (passes data to useFetch)
import React from "react";
import {useFetchPokemon} from '@hooks/useFetchPokemon'
export const PokemonForm: React.FC = () => {
const {input, handleChange, handleSubmit} = useFetchPokemon()
return (
<form className="sfr-form" onSubmit={handleSubmit}>
<input name="name" value={input.name} placeholder="boas" onChange={(e)=> handleChange(e)}/>
<button type="submit">fetch</button>
</form>
);
};
Fetch Hook: (fetches the data)
import React, {useState, useEffect} from 'react'
import {usePokemon} from "@context/PokemonContext";
export const useFetchPokemon = () => {
const [newPokemon, setNewPokemon] = useState<Pokemon | string | null>(() => '');
const [input, setInput] = useState({name: '', submited: false})
const {getPokemon} = usePokemon()
getPokemon(newPokemon as Pokemon)
useEffect(() => {
const getData = async () => {
try{
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${input.name}`).then((res)=>res.json())
setNewPokemon({id: response.id, name: response.name, type:response.types[0].type.name, imageUrl: response.species.url})
} catch (error) {
console.log('error', error)
}
};
getData();
return () => {
setNewPokemon('')
setInput({name: '', submited: false})
};
}, [input.name, input.submited]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInput({...input, name: event.target.value})
}
const handleSubmit = async (event: { preventDefault: () => void; }) => {
event.preventDefault();
setInput({...input, submited: true});
}
return {newPokemon, handleChange, handleSubmit, input}
}
useStorageHook (to storage):
import { useEffect, useState } from "react"
export function useLocalStorage<T>(key: string, initialValue: T | (() => T)) {
const [value, setValue] = useState<T>(() => {
const local = localStorage.getItem(key)
if (local != null) return JSON.parse(local)
return typeof initialValue === "function" ? (initialValue as () => T)() : initialValue
})
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value))
}, [key, value])
return [value, setValue] as [typeof value, typeof setValue]
}
List:
import React from "react";
import {usePokemon} from "@context/PokemonContext";
export const PokemonList = () => {
const {pokemon} = usePokemon()
return (
<div className="sfr-list">
{pokemon && [pokemon].map((e, i) => (
<div key={i}>
<pre>
{JSON.stringify(e, null, 2)}
</pre>
</div>
))}
</div>
)
}
Context (sets the data to local storage):
import {createContext, ReactNode, useContext} from "react";
import {useLocalStorage} from "@hooks/useFetchStorage";
type PokemonContext = {
pokemon: {};
getPokemon: ({} : Pokemon) => void
}
const PokemonContext = createContext({} as PokemonContext)
export const usePokemon = () => {
return useContext(PokemonContext)
}
export const PokemonProvider = ({children} : {children: ReactNode}) => {
const [pokemon, setPokemon] = useLocalStorage<Pokemon[]>('pokemon', [{id: 123, name: 'mariomon', type: 'fogo', imageUrl: 'www.google.com'}]);
const getPokemon = async (newlyPokemon : Pokemon) => {
await newlyPokemon && setPokemon(currentPokemons => [...currentPokemons, newlyPokemon])
}
return <PokemonContext.Provider value={{getPokemon, pokemon}}>
{children}
</PokemonContext.Provider>
}
CodePudding user response:
The reason why your pokemon is added infinite times, is the following:
useEffect
inuseFetchPokemon
updatesnewPokemon
state. This causes- the hook
useFetchPokemon
to be re-rendered. During this render getPokemon
is called withnewPokemon
, updating the state ofPokemonProvider
, which causes- the context
PokemonProvider
to re-render, which in turn - causes all children of
PokemonProvider
to re-render, which takes us back to 3) and we have a loop.
You don't want any calls to setState
to be outside of a useEffect
as that almost always will cause infinite loops.