javascript - while trying to fetch data,i get "Unhandled Rejection (TypeError): undefined is not iterable"
问题描述
I am noob at this, so what i am trying to do is fetch the data of pokeapi.co (pokemon api), so i get an an array with objects,each object has an url, this url you can fetch it and get all the info about the current pokemon...It works fine until i try to map the array with all the object inside and shows the following error...(i tried to use async/await buti not sure how to use it...Help!)
import Card from "./card"
import React,{useState,useEffect} from "react"
function App() {
const [pokemon,setPokemon]=useState()
const[loading,setLoading]=useState(true)
useEffect(
async ()=>{
return await fetch("https://pokeapi.co/api/v2/ability/?limit=20&offset=20")
.then(res=> res.json())
.then( async data=>{return await data.results.map(async element=>{return await fetch(element.url).then(res=>res.json()).then(data=>{return setPokemon(prevState=>{return [...prevState,{data}]})})})})}
,[])
return (
<div className="App">
<header id="header" className="py-10 w-full bg-yellow-500">
<h1 className="text-4xl text-center font-bold text-red-700">Pokedex.</h1>
</header>
{loading && <div class="rounded-t-lg overflow-hidden border-t border-l border-r text-center p-4 py-12">
<span class="inline-flex rounded-md shadow-sm">
<button type="button" class="inline-flex items-center px-4 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150 cursor-not-allowed" disabled="">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Processing
</button>
</span>
</div>}
<div className="grid grid-cols-4 gap-4 my-10 px-10">
{pokemon? pokemon.results.map((element,index)=>{return <Card key={index} name={ element.name.toUpperCase()} />}):null}
</div>
<footer className="text-center text-gray-500 font-bold">2020</footer>
</div>
);
}
export default App;
when i fetch this(https://pokeapi.co/api/v2/ability/?limit=20&offset=20) i get this So i try to fetch each url inside the object to get all the info about the current pokemon, so the response is this and what i want to do is save this data in state to render it later
im lost with the usage of async/await... If you have a better idea how to implement this, would be great!!
解决方案
Issues
- Effect hook callback are synchronously processed, they can't be
async
pokemon
initial state is undefined, so it isn't iterablesetPokemon
treatspokemon
state like it is an array, butpokemon
state is later accessed as if it were an object, i.e.pokemon.results
- The loading state is never cleared
- All the pokemon
data
is nested in adata
key, but accessed as if the data were all at the root level, i.e.element.name.toUpperCase()
minor issues
- Use
className
versusclass
in JSX - Use camelCased attributes, i.e.
strokeWidth
versusstroke-width
Solution
Provide initial state, I suggest an array. Now the previous state is iterable
const [pokemon, setPokemon] = useState([]);
Render the array directly. Try to avoid using an array index as the react key.
{pokemon.map((element) => <Card key={element.id} name={element.name.toUpperCase()} />)}
Stick to promise chain, or async/await.
useEffect
using promise chain. Add a catch block for handling errors, and a finally block to clear the loading state.
useEffect(() => {
fetch("https://pokeapi.co/api/v2/ability/?limit=20&offset=20")
// Return response JSON object promise
.then((res) => res.json())
// Map array of fetch request promises
.then((data) => Promise.all(data.results.map((el) => fetch(el.url))))
// Map array of response JSON object promises
.then((results) => Promise.all(results.map((res) => res.json())))
// Update state, copy old state and append new
.then((data) => setPokemon((pokemon) => [...pokemon, ...data]))
.catch((error) =>
console.error("There has been a problem with your fetch operation:", error)
)
.finally(() => setLoading(false));
}, []);
useEffect
using async/await. Wrap all the fetching logic within an async
function and invoke that in the effect hook callback. Add a catch block for handling errors, and a finally block to clear the loading state.
useEffect(() => {
const pokeFetch = async () => {
try {
const res = await fetch("https://pokeapi.co/api/v2/ability/?limit=20&offset=20");
// Await response JSON object promise
const data = await res.json();
// Await map array of fetch request promises
const urlRes = await Promise.all(data.results.map((el) => fetch(el.url)));
// Await map array of response JSON object promise
const results = await Promise.all(urlRes.map((res) => res.json()));
// Update state, copy old state and append new
setPokemon((pokemon) => [...pokemon, ...results]);
} catch (error) {
console.error("There has been a problem with your fetch operation:", error);
} finally {
setLoading(false);
}
};
pokeFetch();
}, []);
JSX render. Use className
and strokeWidth
attributes. Map pokemon
unconditionally, the array.prototype.map
will handle the empty array state without issue.
return (
<div className="App">
<header id="header" className="py-10 w-full bg-yellow-500">
<h1 className="text-4xl text-center font-bold text-red-700">
Pokedex.
</h1>
</header>
{loading && (
<div className="rounded-t-lg overflow-hidden border-t border-l border-r text-center p-4 py-12">
<span className="inline-flex rounded-md shadow-sm">
<button
type="button"
className="inline-flex items-center px-4 py-2 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150 cursor-not-allowed"
disabled=""
>
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Processing
</button>
</span>
</div>
)}
<div className="grid grid-cols-4 gap-4 my-10 px-10">
{pokemon.map((element) => (
<Card key={element.id} name={element.name.toUpperCase()} />
))}
</div>
<footer className="text-center text-gray-500 font-bold">2020</footer>
</div>
);
推荐阅读
- php - 将数组转换为十进制
- postgresql - 仅计算和显示特定列中只有 1 个结果的行
- amazon-web-services - 在同一个 lambda 函数中从不同帐户访问两个表
- python-2.7 - 命令“python setup.py egg_info”在 PATH/psycopg2 中失败,错误代码为 1
- c# - 如何使用值数组读取 appsettings.json
- vue-storefront - 将 Vue 插件(使用 vue.use() 形式的 ui 元素)添加到 vue-storefront 代码库的首选方法是什么?
- javascript - 如何通过 html“按钮单击”调用“JS 文件”并且 JS 文件正在使用 shell.js 运行 bash 脚本
- php - 如何显示来自 PHPSECLIB 的 sftp-get 的 PDF 返回
- dart - 如何阻止我的应用返回启动画面
- regex - 正则表达式的计算复杂度