1
votes

i was playing with an API and making a react app for data display. After sending my info (via form) for the request (via axios), i setted the state using setState method of React Hooks, but that is not working very well, this creates a rare behavior. I know i should use use Effect but i dont know how.

this is the part of the code where i set the state:

function Home() {
  const [search, setSearch] = useState('')
  const [visibleMsj, setVisibleMsj] = useState(false)
  const [pokemon, setPokemon] = useState({})

  const handleSearch = async (e) => {
    e.preventDefault();
    if(search === ''){
      setVisibleMsj(true)
    } else {
      setVisibleMsj(false)
      searchForPokemon()
    }
  };

  const searchForPokemon = async () => {
    const res = await getPokemon(search)
    setPokemon(res)
    console.log(pokemon)
  }

This is the function that send the request (in another file):

import axios from "axios";

const getPokemon = async (name) => {
  return await axios
    .get(`https://pokeapi.co/api/v2/pokemon/${name}`)
    .then(function (response) {
      return response.data;
    })
    .catch(function (err) {
      console.log(err);
    });
};

export { getPokemon };

and finally, the form who executes the method in submit event:

<Form onSubmit={handleSearch} id="form">
          <Form.Field>
            <Form.Input
              icon="search"
              placeholder="Search by name or id..."
              name="pokemon"
              onChange={(event) => setSearch(event.target.value)}
              value={search}
            />
          </Form.Field>
</Form>

But in the console i get this when i send the request: Console in web browser

So, what is happening with the state?. Also, i need to display atributtes of the state variable 'pokemon' below the form (inside render) and i cant get these attributes, maybe because the first value when mounting is {}?.

Thanks in advance.

2
Need more information on what you are trying to do and what you are expecting to see. The data looks fine to me. Also note that setState is asynchronous, so the state will most likely not have been updated when you console.log it. You need to useEffect(()=>{/* code */}, [pokemon]) where code is probably going to be a call to getPokemon - user120242
And in example, if i need to put the value {pokemon.species.name} inside the return method (below the form), i need to check if it exists first?. That is all i want, see that property visualized. - joacof98

2 Answers

0
votes

You need to make the call to the api only when the particular value in the state changes. You can react to those changes using useEffect, it accepts a second array parameter in which you can add state values you want to react to when they change.

function Home() {
    const [search, setSearch] = useState('')
    const [visibleMsj, setVisibleMsj] = useState(false)
    const [pokemon, setPokemon] = useState({})

    useEffect(() => {
       if(search === ''){
         setVisibleMsj(() => true)
       } else {
          setVisibleMsj(() => false)
          searchForPokemon()
       }
    },[search])

   const searchForPokemon = async () => {
      const res = await getPokemon(search)
      setPokemon(() => res)
   }

   ...

}

Side Note: I will advice you use the functional pattern in setting state. React batches the state updates. Using a function makes sure you always get the latest updates eg: setState((prevState) => prevState + 1)

0
votes

You can also put the data fetching function to the useCallback hook, so the callback will change only when the state specified in [...] gets updated. Then, you can pass that callback to the useEffect hook. You can't just pass a regular callback to the useEffect's dependencies list, since it will change every render, so the useEffect hook will fire on every render as well.

`const fetchData = useCallback(() => YourAsyncFunction, [variables, that, change, in that function]);

const useEffect(() => {
//this will fire only when fetchData gets updated
fetchData();
}, fetchData);

`