в какой-то момент данные становятся undefined после установки состояния через хук Reactjs
Изучаю react самостоятельно. поясните пожалуйста, из-за чего происходит такая ситуация. PS: заранее извините меня за "многабукаф", пытался максимально понятно объяснить проблему. спасибо. Суть дела: Устанавливаю начальное состояние через хук:
const [pokemon, setPokemon] = useState({
img: "",
name: "",
types: [],
abilities: [],
moveList: [],
weight: "",
height: "",
description: "",
genus: "",
chanceToCatch: "",
evolutionURL: ""
});
далее делаю запросы api для получения информации с помощью useEffect:
useEffect(() => {
const fetchData = async () => {
await Axios({
method: "GET",
url: urlPokemonAPI
})
.then(result => {
const pokemonResponse = result.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
})
await Axios({
method: "GET",
url: urlPokemonSpecies
}).then(result => {
let description = "";
result.data.flavor_text_entries.some(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
return description;
});
let genus = "";
result.data.genera.some(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
return genus;
});
const evolutionURL = result.data.evolution_chain.url;
const eggGroups = result.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(result.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
Проблема возникает конкретно с eggGroups(с идентичной обработкой abilities и types такой проблемы нет). А происходит вот что, когда я хочу вывести данные на страницу как <div>Egg Group: {pokemon.eggGroups}</div> данные выводятся нормально, но как только я хочу вывести как abilities и types через запятую, возникает ошибка TypeError: pokemon.eggGroups is undefined. Решил я это дело проверить через консоль и запихнул этот ключ eggGroups в таймаут.
В какой-то момент eggGroups становится undefined... почему, не могу понять. Но если установлю состояние отдельно, как const [egg, setEgg] = useState([]); setEgg(eggGroups); такой проблемы не наблюдается. из-за чего так происходит? с types и abilities было все нормально.
Заранее благодарю.
UDP!!! Вопрос разрешен. Проблема заключалась вот в чем: когда я обновлял стейт первый раз:
setPokemon(() => {
return {
....
};
});
я перезаписывал все, что было объявлено в const [pokemon, setPokemon] = useState({ ... }). а затем когда я ниже опять обращался к стейту
setPokemon(pokemon => {return { ...pokemon, ...}});
как раз в нем я обращался к ключу eggGroups: eggGroups, но ключа eggGroups уже не было.
Закину обновленный вариант кода, может быть кому-нибудь пригодится
import Axios from "axios";
const Pokemon = props => {
const [pokemon, setPokemon] = useState({
img: "",
name: "",
weight: "",
height: "",
description: "",
genus: "",
types: [],
abilities: [],
moveList: [],
eggGroups: [],
stats: {},
chanceToCatch: "",
evolutionURL: ""
});
const pokemonNameForAPI = props.match.params.pokemonName.toLowerCase();
const urlPokemonAPI = `https://pokeapi.co/api/v2/pokemon/${pokemonNameForAPI}/`;
const urlPokemonSpeciesAPI = `https://pokeapi.co/api/v2/pokemon-species/${pokemonNameForAPI}/`;
useEffect(() => {
const fetchData = async () => {
try {
const resultPokemonAPI = await Axios.get(urlPokemonAPI);
const dataPokemonAPI = resultPokemonAPI.data;
const resultPokemonSpeciesAPI = await Axios.get(urlPokemonSpeciesAPI);
const dataPokemonSpeciesAPI = resultPokemonSpeciesAPI.data;
/* Pokemon Information */
const img = dataPokemonAPI.sprites.front_default;
const name = dataPokemonAPI.name;
const weight = Math.round(dataPokemonAPI.weight / 10); // hectograms->kilogram
const height = dataPokemonAPI.height / 10; // decimeters->meters
function getDescription(data) {
let result = "";
data.flavor_text_entries.forEach(flavor => {
if (flavor.language.name === "en") {
result = flavor.flavor_text;
}
});
return result;
}
const description = getDescription(dataPokemonSpeciesAPI);
function getGenus(data) {
let result = "";
data.genera.forEach(genera => {
if (genera.language.name === "en") {
result = genera.genus;
}
});
return result;
}
const genus = getGenus(dataPokemonSpeciesAPI);
const types = dataPokemonAPI.types.map(type => type.type.name);
const abilities = dataPokemonAPI.abilities.map(
ability => ability.ability.name
);
const moveList = dataPokemonAPI.moves.map(move => move.move.name);
function getStats(data) {
const result = {};
data.stats.map(stat => {
result[stat.stat.name] = stat.base_stat; // add keys: speed, special-defense, special-attack, attack, hp
});
return result;
}
const stats = getStats(dataPokemonAPI);
const eggGroups = dataPokemonSpeciesAPI.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(dataPokemonSpeciesAPI.capture_rate * 100) / 255 // 255/100=dataPokemonSpeciesAPI.capture_rate/x
);
const evolutionURL = dataPokemonSpeciesAPI.evolution_chain.url;
/* ****** */
/* update State */
setPokemon(prev => {
return {
...prev,
img: img,
name: name,
weight: weight,
height: height,
description: description,
genus: genus,
types: types,
abilities: abilities,
moveList: moveList,
eggGroups: eggGroups,
stats: stats,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL
};
});
} catch (error) {
console.log(error);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpeciesAPI]);
Ответы (2 шт):
скорее всего блоки then отрабатывают не синхронно. попробуй в первом setPokemon скопировать предыдущее состояние стейта:
setPokemon((pokemon) => {
return {
...pokemon,
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
либо переписать код без блоков then
useEffect(() => {
const fetchData = async () => {
const firstRes = await Axios({
method: "GET",
url: urlPokemonAPI
})
const pokemonResponse = firstRes.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
const secondRes = await Axios({
method: "GET",
url: urlPokemonSpecies
})
let description = "";
secondRes.data.flavor_text_entries.some(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
return description;
});
let genus = "";
secondRes.data.genera.some(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
return genus;
});
const evolutionURL = secondRes.data.evolution_chain.url;
const eggGroups = secondRes.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(secondRes.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
О это типичные грабли на которые наступает каждый )
Внимательно посмотри на все вызовы setState() каждый должен содержать ...state
