将对象数组映射到 React Elements 时,其中一个属性消失了
While mapping an array of objects to React Elements, one of the properties disappears
我正在尝试制作一个 React 应用程序以从宠物小精灵 api (https://pokeapi.co/docs/v2#pokemon) 获取数据并将其显示在网格中。
我的组件树:
应用程序 -> Pokedex -> n(PokeCard -> PokeAvatar)
在 App 组件中,我获取了 pokemon 数据,我得到了 20 个第一个结果并将结果数组映射到一个数组,其中每个 Pokemon 的 URL 也被获取(数组中的每个对象都有'name'、'url' 和 'info' 属性)。信息 属性 包含每个单独提取的所有数据。
在使用此数组作为道具渲染 Pokedex 之后,在 Pokedex 组件中,我将数组映射到仅包含我要显示的数据(名称和 'info' 中的一些属性)的元素数组属性).
这是引发错误的代码:
export class Pokedex extends React.Component {
render() {
console.log("this.props.pokeArray:", this.props.pokeArray); // shows pokemons with the 3 properties
const elements = this.props.pokeArray.map((pokemon, i) => {
console.log(pokemon); // logs the pokemon without the info property
return (
<PokeCard
key={`poke${i}`}
id={pokemon.info.id} //error raised here: Cannot read property 'id' of undefined
name={pokemon.name}
types={pokemon.info.types}
sprite={pokemon.info.sprites["front_default"]}
/>
);
});
return (
<div className="pkdx-pokedex-container">
<h1>Pokedex</h1>
{elements}
</div>
);
}
}
这也是来自其父元素 App 的代码:
import "./App.css";
import { Pokedex } from "./components/Pokedex/Pokedex";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query-devtools";
// *** Base API url
const url = "https://pokeapi.co/api/v2/pokemon";
// *** Async, because we need to have the data before second fetch
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
// *** Keep url of fetch and add new info property to each pokemon
data.forEach(async (poke) => {
const res = await fetch(poke.url);
poke.info = await res.json();
});
return data;
}
function App() {
const info = useQuery("fetchPokemon", fetchPokemon);
if (info.status === "success") {
console.log("pokeArray:", info.data); // each Pokemon has the three properties
return (
<div>
<Pokedex pokeArray={info.data} />;
<ReactQueryDevtools />
</div>
);
} else return null;
}
export default App;
我不知道我是否遗漏了什么,但我不明白为什么它不显示 'info' 属性.
看起来您正在执行异步 forEach
而不是等待它。您可能需要更改为 map
并执行 const data = await Promise.all(data.map(...))
以确保您的数据已加载。
我整理了一个working example。看看:
import React from "react";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query-devtools";
export class Pokedex extends React.Component {
render() {
console.log("this.props.pokeArray:", this.props.pokeArray); // shows pokemons with the 3 properties
const elements = this.props.pokeArray.map((pokemon, i) => {
console.log("POKEMON", pokemon); // logs the pokemon without the info property
return (
<React.Fragment key={i}>
<div>key={`poke${i}`}</div>
<div>id={pokemon.info.id}</div>
<div>name={pokemon.name}</div>
<div>sprite={pokemon.info.sprites["front_default"]}</div>
</React.Fragment>
);
});
return (
<div className="pkdx-pokedex-container">
<h1>Pokedex</h1>
{elements}
</div>
);
}
}
// *** Base API url
const url = "https://pokeapi.co/api/v2/pokemon";
// *** Async, because we need to have the data before second fetch
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
// *** Keep url of fetch and add new info property to each pokemon
const x = await Promise.all(
data.map(async (poke) => {
const res = await fetch(poke.url);
return {
...poke,
info: await res.json()
};
})
);
return x;
}
function App() {
const info = useQuery("fetchPokemon", fetchPokemon);
if (info.status === "success") {
console.log("pokeArray:", info.data); // each Pokemon has the three properties
return (
<div>
<Pokedex pokeArray={info.data} />;
<ReactQueryDevtools />
</div>
);
} else return null;
}
export default App;
这里的问题在于fetchPokemon
。在 forEach
回调中使用 await fetch(poke.url)
时,回调将愉快地等待响应。但是 forEach
不处理回调所承诺的 return。这意味着 pokemon.info
属性是在 data
从 fetchPokemon
函数 return 编辑后的某个时间设置的。
要解决此问题,请使用 map()
to store the resulting promises, then Promise.all()
等待要解决的承诺列表。
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
await Promise.all(data.map(async (pokemon) => {
const res = await fetch(pokemon.url);
pokemon.info = await res.json();
}));
return data;
}
异步函数始终return是一个承诺,因此将异步函数传递给map()
会将当前数组元素映射到承诺。然后可以将该数组传递给 Promise.all()
,它将等待所有承诺完成。承诺将全部解析为 undefined
,因为 map()
回调中没有 return 值,但是数据存储在 data
中,因此我们可以 return相反。
const url = "https://pokeapi.co/api/v2/pokemon";
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
await Promise.all(data.map(async (pokemon) => {
const res = await fetch(pokemon.url);
pokemon.info = await res.json();
}));
return data;
}
fetchPokemon().then(pokemon => {
document.body.textContent = JSON.stringify(pokemon);
});
我正在尝试制作一个 React 应用程序以从宠物小精灵 api (https://pokeapi.co/docs/v2#pokemon) 获取数据并将其显示在网格中。
我的组件树:
应用程序 -> Pokedex -> n(PokeCard -> PokeAvatar)
在 App 组件中,我获取了 pokemon 数据,我得到了 20 个第一个结果并将结果数组映射到一个数组,其中每个 Pokemon 的 URL 也被获取(数组中的每个对象都有'name'、'url' 和 'info' 属性)。信息 属性 包含每个单独提取的所有数据。
在使用此数组作为道具渲染 Pokedex 之后,在 Pokedex 组件中,我将数组映射到仅包含我要显示的数据(名称和 'info' 中的一些属性)的元素数组属性).
这是引发错误的代码:
export class Pokedex extends React.Component {
render() {
console.log("this.props.pokeArray:", this.props.pokeArray); // shows pokemons with the 3 properties
const elements = this.props.pokeArray.map((pokemon, i) => {
console.log(pokemon); // logs the pokemon without the info property
return (
<PokeCard
key={`poke${i}`}
id={pokemon.info.id} //error raised here: Cannot read property 'id' of undefined
name={pokemon.name}
types={pokemon.info.types}
sprite={pokemon.info.sprites["front_default"]}
/>
);
});
return (
<div className="pkdx-pokedex-container">
<h1>Pokedex</h1>
{elements}
</div>
);
}
}
这也是来自其父元素 App 的代码:
import "./App.css";
import { Pokedex } from "./components/Pokedex/Pokedex";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query-devtools";
// *** Base API url
const url = "https://pokeapi.co/api/v2/pokemon";
// *** Async, because we need to have the data before second fetch
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
// *** Keep url of fetch and add new info property to each pokemon
data.forEach(async (poke) => {
const res = await fetch(poke.url);
poke.info = await res.json();
});
return data;
}
function App() {
const info = useQuery("fetchPokemon", fetchPokemon);
if (info.status === "success") {
console.log("pokeArray:", info.data); // each Pokemon has the three properties
return (
<div>
<Pokedex pokeArray={info.data} />;
<ReactQueryDevtools />
</div>
);
} else return null;
}
export default App;
我不知道我是否遗漏了什么,但我不明白为什么它不显示 'info' 属性.
看起来您正在执行异步 forEach
而不是等待它。您可能需要更改为 map
并执行 const data = await Promise.all(data.map(...))
以确保您的数据已加载。
我整理了一个working example。看看:
import React from "react";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query-devtools";
export class Pokedex extends React.Component {
render() {
console.log("this.props.pokeArray:", this.props.pokeArray); // shows pokemons with the 3 properties
const elements = this.props.pokeArray.map((pokemon, i) => {
console.log("POKEMON", pokemon); // logs the pokemon without the info property
return (
<React.Fragment key={i}>
<div>key={`poke${i}`}</div>
<div>id={pokemon.info.id}</div>
<div>name={pokemon.name}</div>
<div>sprite={pokemon.info.sprites["front_default"]}</div>
</React.Fragment>
);
});
return (
<div className="pkdx-pokedex-container">
<h1>Pokedex</h1>
{elements}
</div>
);
}
}
// *** Base API url
const url = "https://pokeapi.co/api/v2/pokemon";
// *** Async, because we need to have the data before second fetch
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
// *** Keep url of fetch and add new info property to each pokemon
const x = await Promise.all(
data.map(async (poke) => {
const res = await fetch(poke.url);
return {
...poke,
info: await res.json()
};
})
);
return x;
}
function App() {
const info = useQuery("fetchPokemon", fetchPokemon);
if (info.status === "success") {
console.log("pokeArray:", info.data); // each Pokemon has the three properties
return (
<div>
<Pokedex pokeArray={info.data} />;
<ReactQueryDevtools />
</div>
);
} else return null;
}
export default App;
这里的问题在于fetchPokemon
。在 forEach
回调中使用 await fetch(poke.url)
时,回调将愉快地等待响应。但是 forEach
不处理回调所承诺的 return。这意味着 pokemon.info
属性是在 data
从 fetchPokemon
函数 return 编辑后的某个时间设置的。
要解决此问题,请使用 map()
to store the resulting promises, then Promise.all()
等待要解决的承诺列表。
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
await Promise.all(data.map(async (pokemon) => {
const res = await fetch(pokemon.url);
pokemon.info = await res.json();
}));
return data;
}
异步函数始终return是一个承诺,因此将异步函数传递给map()
会将当前数组元素映射到承诺。然后可以将该数组传递给 Promise.all()
,它将等待所有承诺完成。承诺将全部解析为 undefined
,因为 map()
回调中没有 return 值,但是数据存储在 data
中,因此我们可以 return相反。
const url = "https://pokeapi.co/api/v2/pokemon";
async function fetchPokemon() {
const response = await fetch(url);
const data = (await response.json()).results;
await Promise.all(data.map(async (pokemon) => {
const res = await fetch(pokemon.url);
pokemon.info = await res.json();
}));
return data;
}
fetchPokemon().then(pokemon => {
document.body.textContent = JSON.stringify(pokemon);
});