Promise 仅在页面刷新时正确解析
Promise only resolves correctly on page refresh
我正在玩弄一个 API,它获取 Pokemon 列表和相应的数据,如下所示。
export function SomePage() {
const [arr, setArray] = useState([]);
useEffect(() => {
fetchSomePokemon();
}, []);
function fetchSomePokemon() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=5')
.then(response => response.json())
.then((pokemonList) => {
const someArray = [];
pokemonList.results.map(async (pokemon: { url: string; }) => {
someArray.push(await fetchData(pokemon))
})
setArray([...arr, someArray]);
})
}
async function fetchData(pokemon: { url: string; }) {
let url = pokemon.url
return await fetch(url).then(async res => await res.json())
}
console.log(arr);
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div
key={index}
>
{pokemon.name}
</div>
))
}
</div>
);
}
代码有效(有点),但是在第一次渲染时,即使 console.log 输出数据,地图也不会显示任何内容。只有刷新页面后,才会显示正确的数据。我觉得这与没有正确处理承诺有关。也许有人可以帮助我。
TIA
预期输出:初始渲染时填充的数据(在这种情况下,将显示 pokemon 名称)
在
useEffect(() => { fetchSomePokemon(); }, []);
[]
告诉 React 这个效果的发生没有依赖关系,
在此处阅读更多内容 https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
在您的第一次渲染中,您还没有数据,因此 arr[0]
不存在,您无法在其上 .map
,因此它崩溃了。映射前需要检查数据是否已经存在
使用 optional chaining,如果没有数据,它不会在您的第一次渲染时抛出错误,并且当数据到达时它会正确渲染并且 re-renders。
...
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div key={index}>{pokemon.name}</div>
))}
</div>
);
}
in-build map
数组上的方法本质上是同步的。在 fetchSomePokemon
中,您需要 return 来自地图回调函数的承诺,因为您正在其中编写异步代码。
现在 pokemonList.results.map
编辑的数组 return 中的项目是承诺。您需要在 pokemonList.results.map
和 await
上使用 Promise.all
。
await Promise.all(pokemonList.results.map(async (pokemon: { url: string; }) => {
return fetchData.then(someArray.push(pokemon))
}));
解决问题的一种方法是 await
在 useEffect()
中获取数据。
这是一个 POC:
export function Page() {
const [pokemon, setPokemon] = useState([]);
// will fetch the pokemon on the first render
useEffect(() => {
async function fetchPokemon() {
// ... logic that fetches the pokemon
}
fetchPokemon();
}, []);
if (!pokemon.length) {
// you can return a spinner here
return null;
}
return (
<div>
{pokemon.map(item => {
// return an element
})}
</div>
);
}
我正在玩弄一个 API,它获取 Pokemon 列表和相应的数据,如下所示。
export function SomePage() {
const [arr, setArray] = useState([]);
useEffect(() => {
fetchSomePokemon();
}, []);
function fetchSomePokemon() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=5')
.then(response => response.json())
.then((pokemonList) => {
const someArray = [];
pokemonList.results.map(async (pokemon: { url: string; }) => {
someArray.push(await fetchData(pokemon))
})
setArray([...arr, someArray]);
})
}
async function fetchData(pokemon: { url: string; }) {
let url = pokemon.url
return await fetch(url).then(async res => await res.json())
}
console.log(arr);
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div
key={index}
>
{pokemon.name}
</div>
))
}
</div>
);
}
代码有效(有点),但是在第一次渲染时,即使 console.log 输出数据,地图也不会显示任何内容。只有刷新页面后,才会显示正确的数据。我觉得这与没有正确处理承诺有关。也许有人可以帮助我。 TIA
预期输出:初始渲染时填充的数据(在这种情况下,将显示 pokemon 名称)
在
useEffect(() => { fetchSomePokemon(); }, []);
[]
告诉 React 这个效果的发生没有依赖关系,
在此处阅读更多内容 https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
在您的第一次渲染中,您还没有数据,因此 arr[0]
不存在,您无法在其上 .map
,因此它崩溃了。映射前需要检查数据是否已经存在
使用 optional chaining,如果没有数据,它不会在您的第一次渲染时抛出错误,并且当数据到达时它会正确渲染并且 re-renders。
...
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div key={index}>{pokemon.name}</div>
))}
</div>
);
}
in-build map
数组上的方法本质上是同步的。在 fetchSomePokemon
中,您需要 return 来自地图回调函数的承诺,因为您正在其中编写异步代码。
现在 pokemonList.results.map
编辑的数组 return 中的项目是承诺。您需要在 pokemonList.results.map
和 await
上使用 Promise.all
。
await Promise.all(pokemonList.results.map(async (pokemon: { url: string; }) => {
return fetchData.then(someArray.push(pokemon))
}));
解决问题的一种方法是 await
在 useEffect()
中获取数据。
这是一个 POC:
export function Page() {
const [pokemon, setPokemon] = useState([]);
// will fetch the pokemon on the first render
useEffect(() => {
async function fetchPokemon() {
// ... logic that fetches the pokemon
}
fetchPokemon();
}, []);
if (!pokemon.length) {
// you can return a spinner here
return null;
}
return (
<div>
{pokemon.map(item => {
// return an element
})}
</div>
);
}