1

I don't understand why my "console.log(champion)" return nothing ... Someone can explain me why the asynchrone function don't work ? Isn't setCahmp supposed to change the value of "champions"? I guess it because axios take sometime to search datas... I don't know how I could fix it. And then I would like to map "champion" but its an object, how I could do that ?

Thans you

import React, { useEffect, useState } from "react";
import axios from "axios";

const Champs = () => {
  const [champions, SetChampions] = useState([]);

  useEffect(() => {
    axios
      .get(
        "http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion.json"
      )
      .then((res) => {
        SetChampions(res.data.data);
        console.log(res.data.data);
      })
      .then(
        console.log(champions)
      );
  }, []);

  return (
    <div className="champs">
      {/* {champions.map((champ) => {
        return <p> {champ.id}</p>;
      })} */}
    </div>
  );
};

export default Champs;
  • try to remove the second then on your axios call – Romylussone Apr 12 '22 at 18:49
  • 1
    I took a look at the data returned from champion.json - I don't see a `data.data` property, so `res.data.data` probably won't work (maybe try res.data?). Also, `data` is an object with keys, not an array, so map won't work as is, you might have to take `Object.values(res.data)`. – James Apr 12 '22 at 18:50
  • Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – Brian Thompson Apr 12 '22 at 18:52
  • Even though you're using `.then`, `champions` is closed over. You're experiencing the stale closure problem. It doesn't matter how long you wait before trying to log, it will always be the same value until you check it during the *next* render. – Brian Thompson Apr 12 '22 at 18:54
  • Thank you for your answers and explanation. that work, I just have to use Object.values to map :) – thibault schmitt Apr 12 '22 at 19:12

3 Answers3

2

In your API response response.data.data is not an array of objects, it's nested objects and you are initializing the champions as an array. So, setChampions can't assign an object to an array.

Also, you can't use the map function to loop an object. You can use Object.keys to map the response.

SoftNinja
  • 21
  • 6
1

You shouldn't do a double "then" on your code. If you want to know when the state champions is set you should use a second useEffect with "champions" in param :

  useEffect(() => {
    axios
      .get(
        "http://ddragon.leagueoflegends.com/cdn/12.5.1/data/en_US/champion.json"
      )
      .then((res) => {
        SetChampions(res.data.data);
        console.log(res.data.data);
      });
  }, []);

  useEffect(() => {
    console.log(champions)
  }, [champions]);

If you want to map an object you should do this :

<div className="champs">
  {Object.keys(champions).map((key) => {
    const champ = champions[key]
    return <p> {champ.id}</p>;
  })}
</div>

Object.keys will return an array of key of your object, so you can map it. And to access to the value you can simply use the key like this : const champ = champions[key]

Hoping that can help you in your research

0

It could be that console.log(champion) isn't working because it's getting called before SetChampion is completed. You don't need the 2nd .then() call to check on champion. To make sure champion is getting set, you could make a useEffect that is called whenever champion gets set, like so:

useEffect(() => {
  console.log(champion);
}, [champion])

This will get called when champion is initially set to [] with useState, and then when you set it with SetChampions().

Travis
  • 61
  • 1
  • 6
  • 1
    `SetChampion` doesn't "resolve". Yes it is asynchronous, but not in the sense that it returns a Promise that can be awaited. That said, your solution will show the correct result – Brian Thompson Apr 12 '22 at 18:55
  • Thanks! That is the wrong word to use, I will edit my answer. – Travis Apr 12 '22 at 18:56
  • having 'champion' in the dependency array may cause infinite loops. I think OPs JSX needs to conditionally render `champion` or something else i.e. loading state, skeleton ui, etc. – Hyetigran Apr 12 '22 at 19:01
  • @Hyetigran this will not cause an infinite loop... It's just a console log. – Brian Thompson Apr 12 '22 at 19:03
  • @BrianThompson I thought this was original useEffect. Not sure what point this one serves? You could just place the log in the component and it'll print on ever render. – Hyetigran Apr 12 '22 at 19:04
  • 1
    When using `StrictMode` components double render and beginning in React 18 it no longer suppresses console logs from those extra renders. Putting it in a `useEffect` will correctly log the value of `champions` once whenever it changes. It's a valid use-case for debugging – Brian Thompson Apr 12 '22 at 19:06
  • 1
    @BrianThompson Hmm, I see. Good to know. – Hyetigran Apr 12 '22 at 19:10