Blank page error when using search in react - javascript

I have watched countless videos and tried to implement suggestions from here and edited the code more than I can remember. Could someone please let me know what I am doing wrong here? I am trying to get the search to go through the data and filter the cards.
The data loads fine but there are some errors in console
When I try searching, once I type anything letter. The page goes blank
And console looks like this, Cannot read properties of undefined (reading 'filter') is suggested to be an error if item received is not an array, but the results shows array.
.
Here is my code. I am trying to learn so I understand this is very messy and should be in components.
import React from 'react';
import { useState } from 'react';
import axios from 'axios';
import { useEffect } from 'react';
function App() {
const [data, setData] = React.useState([]);
// const [searchTerm, setSearchTerm] = React.useState('');
const [searchTerm, setSearchTerm] = useState('');
const handleChange = event => {
setSearchTerm(event.target.publications);
};
useEffect(() => {
axios.get('https://api.**url.com/api/v1/content/publications?limit=50',{
headers: {
'X-AUTH-TOKEN' : '22bc08a8***2995da615c'
}
})
.then(response => {
console.log(response.data);
setData(response.data.data);
});
}, []);
return(
<div>
<h1>Publication title goes here </h1>
{/* Search */}
<div className='App'>
<input type="text" placeholder='Search' onChange={handleChange}/>
</div>
{/* Search end*/}
<div className="px-10 py-10 grid gap-4 md:grid-cols-4">
{
data.filter((publications) =>{
if (searchTerm == "") {
return publications}
// else if (data.publications.filter(item => item.toLowerCase().includes(searchTerm.toLowerCase())))
{data.publications.filter(item => item.toLowerCase().includes(searchTerm.toLowerCase()))
{
return publications
}
}}).map(publications =>
{ return (
// card
<div className="user" key={publications.id} className="container mx-auto max-w-4xl">
<div class="max-w-sm bg-white border border-gray-200 hover:bg-gray-100 rounded-lg shadow-md dark:bg-gray-800 dark:border-gray-700">
<a href="#">
<img class="rounded-t-lg" src="/docs/images/blog/image-1.jpg" alt="" />
</a>
<div class="p-5">
<a href="#">
<h2 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" />
</svg>
{publications.description}</h2>
</a>
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">Author: {publications.author}.</p>
<a href="#" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Last Publication
<svg aria-hidden="true" class="w-4 h-4 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</a>
</div>
</div>
</div>
)
}
)
}
</div>
</div>
)
}
export default App;

The errors are showing up in your console, because you have to use className instead of class in React. You are using class in your card component. The other HTML attributes have to be written in camel case, so fill-rule becomes fillRule and clip-rule becomes clipRule.
For the issues with your filter, you should have a look at the docs for the Array.filter() and String.includes() methods. You can't filter an object with the includes method.
Also Cannot read properties of undefined (reading 'filter') is showing up, because data.publications IS undefined. Start using Typescript and these errors won't happen again.
Here is an example of how your filter could look like:
data.filter((publication) => {
// If no search term is set, return true
if (!searchTerm) {
return true
}
// If either the publication description or the author contain the search term, return true
if (publication.description.toLowerCase().includes(searchTerm.toLowerCase()) || publication.author.toLowerCase().includes(searchTerm.toLowerCase())) {
return true
}
// If there is a search term and neither the description nor the author contain the search term, return false
return false
})

Related

Pulling and displaying API data to tailwind cards

could someone help on how to get all data from this console log? I am trying to display all the details in individual tailwind cards but not sure how to get the data. Thank you for your time.
axios.get('https://api.url**.com/api/v1/content/publications',{
headers: {
'X-AUTH-TOKEN' : '22***8a8369317***********'
}
}
)
.then(response => {
console.log (response.data)
It works and I get the results below in console.
when I tried console.log (response.data.0.pdf) for example, it gives an error "Parsing error: Unexpected token, expected".
Is it possible to get all the data under publications into individual cards?
<div class="max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow-md dark:bg-gray-800 dark:border-gray-700">
<a href="#">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">**{author}**</h5>
</a>
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
**{pdf}**
**{id}**
**{etc}**</p>
<a href="#" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
Read more
<svg aria-hidden="true" class="w-4 h-4 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</a>
</div>
Thanks again for your time.
From what I can tell it seems you are already fetching all the data since the response is an array. It seems like your question is then how to save and render the response data.
Create a local state variable to hold the fetched data. When this state is updated React will trigger a rerender with the latest state enclosed in scope.
const [data, setData] = React.useState([]);
Fetch the data in a component lifecycle method/hook, e.g. useEffect when the component mounts, and enqueue a state update to save the fetched data.
useEffect(() => {
axios.get('https://api.url**.com/api/v1/content/publications',{
headers: {
'X-AUTH-TOKEN' : '22***8a8369317***********'
}
})
.then(response => {
console.log(response.data);
setData(response.data); // <-- save response to state
});
}, []);
Render the local state to JSX. Arrays are typically mapped to JSX, e.g. Array.prototype.map. Each mapped element object is accessible in the map callback, e.g. item.author. Don't forget to also include a key prop on the outer-most mapped element. The React key is used as part of React's reconciliation process when it's computing what needs to refreshed to the DOM.
{data.map(item => (
<div
key={item.id}
class="max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow-md dark:bg-gray-800 dark:border-gray-700"
>
<a href="#">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
{item.author}
</h5>
</a>
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
{item.pdf}
{item.id}
{item.etc}
</p>
<a
href="#"
class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Read more
<svg
aria-hidden="true"
class="w-4 h-4 ml-2 -mr-1"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</a>
</div>
))}

Update Fetch request

I have a problem with useFetch. I already apologize for my bad English.
Basically I have a button that clicking it must change the value of "cardname" which consequently calls useFetch and changes "cardport". But this is not the case. Can someone help me? Thanks.
This Fetch request
import { useEffect, useState } from 'react';
const useFetch = ({ url , cardname}) => {
const [card, setCard] = useState(null);
useEffect(() => {
fetch(url + cardname)
.then((result) => {
return result.json();
})
.then((data) => {
setCard(data);
});
}, []);
return { card }
}
export default useFetch
And this is the component with the button.
import React from 'react';
import { Link } from 'react-router-dom';
import { useState } from 'react';
import useFetch from '../hooks/useFetchCard';
const SearchBar = () => {
const url = "https://api.scryfall.com/cards/named?fuzzy=";
const [cardname, setCardname] = useState("fireball");
let cardport = useFetch({url , cardname});
function getInputValue() {
let inputVal = document.getElementById("searchtext").value
setCardname(inputVal);
console.log(cardname);
console.log(inputVal)
console.log(cardport)
}
return (
<div className="flex justify-center py-12">
<div className="mb-3 xl:w-96">
<div className="input-group relative flex flex-nowrap items-stretch w-full h-12 mb-4">
<input type="search" className="form-control relative flex-auto min-w-0 block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded-l-lg transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none" placeholder="Search" aria-label="Search" aria-describedby="button-addon2" id="searchtext"/>
<button className="btn inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded-r-lg shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out flex items-center transition-all ease-in-out hover:-translate-y hover:scale-125 hover:rounded duration-300" type="button" id="button-addon2" onClick={getInputValue}>
<Link to="/searchpage"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" className="w-4" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path>
</svg></Link>
</button>
</div>
</div>
</div>
)
}
export default SearchBar

how can I update an input and submit it.?

I Want if I click on the edit icon the text will appear in input and I can edit it and when I submit it will edit not created again.
I did the click on edit icon the text will appear in input but when I submit its created as new, so how can I do that?
here is a gif of what I have i want in submit will be updated
app.js
function App() {
const [tasks, setTask]=useState([])
const [title, setTitle]=useState("")
const [changeTasks , setChangeTasks ] = useState(false)
const [edit, setEdit]=useState(false)
useEffect(() => {
axios.get('/tasks')
.then(data=>setTask(data.data))
.catch(err => console.log(err))
}, [changeTasks]);
const addTask = (e) =>{
e.preventDefault()
axios.post('/tasks/create', {title:title, completed: true})
.then(data => setChangeTasks(pre => !pre))
.catch(err => console.log(err))
// to empty input after submit
setTitle("")
if(edit === true){
// do axios.put, but its in other component how could i do that
}
}
return (
<div className="relative flex min-h-screen bg-gray-200 flex-col justify-center overflow-hidden bg-gray-50 py-6 sm:py-12">
<h1 className="flex justify-center p-4 md:p-0 mx-px md:mt-4 md:mb-3 text-center font-extrabold text-transparent text-8xl bg-clip-text bg-gradient-to-r from-purple-800 to-pink-300">
To Do List
</h1>
<div className="relative bg-white px-6 pt-10 pb-8 shadow-xl ring-1 ring-gray-900/5 sm:mx-auto sm:max-w-lg sm:rounded-lg sm:px-10">
<div className="p-2">
<figure className="mb-4">
<blockquote className="italic font-extralight text-slate-700 mb-5">
<q> To-Do lists help us break life into small steps. </q>
<figcaption className="ml-5 text-sm">—Randy Pausch</figcaption>
</blockquote>
</figure>
<form onSubmit={addTask} >
<input
onChange={({target})=>{
setTitle(target.value)
}}
type="text"
className="p-3 mt-2 mb-4 w-full bg-slate-200 rounded border-2 border-slate-200 focus:border-slate-600 focus:outline-none placeholder:italic"
placeholder="Add Your Task..."
value={title}
/>
</form>
<ul>
{tasks.map(ele => <Task {...ele} setChangeTasks = {setChangeTasks} setTitle={setTitle} setEdit={ setEdit}/>)}
</ul>
</div>
</div>
</div>
);
}
export default App;
Task.jsx
import axios from 'axios'
const Task = ({title, id, completed,setChangeTasks, setTitle, setEdit}) => {
const updateTask =()=>{
axios.put(`/tasks/${id}/update`,{title:title,completed : ! completed })
.then(data => setChangeTasks(pre => !pre))
.catch(err => console.log(err))
}
const deleteTask =()=>{
axios.delete(`/tasks/${id}/delete`)
.then(data => setChangeTasks(pre => !pre))
.catch(err => console.log(err))
}
const editTask =()=>{
axios.put(`/tasks/${id}/update`,{title:title})
.then(data => setChangeTasks())
.catch(err => console.log(err))
setEdit(true)
setTitle(title)
}
return (
<li>
<div className="mt-7 ml-3 flex justify-between">
<label className="inline-flex items-center ">
<input
onClick={updateTask}
type="checkbox"
className=" h-6 w-5 accent-violet-200 hover:accent-violet-200 border-0 rounded-md focus:ring-0"
/>
<span className={ completed ? "ml-4 text-lg" : "ml-4 text-lg line-through"} >{title}</span>
</label>
<div className="">
<button onClick={deleteTask} className="mx-4 text-purple-700 hover:text-white hover:bg-purple-600 p-1 px-2 rounded">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</button>
<button onClick={editTask} class=" text-purple-700 hover:text-white hover:bg-purple-600 p-1 px-2 rounded">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
</button>
</div>
</div>
</li>
)
}
export default Task

Axios response data is not loading on Vue component in Laravel

guys hope y'all doing well! I got a little problem with passing data from axios response to data property. The data fetched from database shows on the console after being fetched but when i want to display it on th component it shows an empty array. Thanks in advance
Vue Component Script:
<script>
import axios from 'axios';
export default {
data(){
return {
Templates:[],
loading:true
}
},
async mounted() {
await axios.post('/getTemplates')
.then(response =>this.Templates =response.data )
.catch(error => console.log(error.message))
console.log(this.Templates)
}
}
</script>
I called the the data prop with {{Templates}} on component :
<article class="overflow-hidden rounded-lg shadow-lg max-w-lg">
<a href="#">
</a>
{{Templates}}
<header class="flex items-center justify-between leading-tight p-2 md:p-4">
<h1 class="text-lg">
<a class="no-underline text-black" href="#">
</a>
</h1>
<p class="text-grey-darker text-sm">
Text
</p>
</header>
<footer class="relative flex items-center justify-between leading-none p-2 md:p-4">
<a class="flex items-center no-underline hover:underline text-black" href="#">
<svg width="24" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12ZM14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12Z" fill="currentColor" /><path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C17.5915 3 22.2898 6.82432 23.6219 12C22.2898 17.1757 17.5915 21 12 21C6.40848 21 1.71018 17.1757 0.378052 12C1.71018 6.82432 6.40848 3 12 3ZM12 19C7.52443 19 3.73132 16.0581 2.45723 12C3.73132 7.94186 7.52443 5 12 5C16.4756 5 20.2687 7.94186 21.5428 12C20.2687 16.0581 16.4756 19 12 19Z" fill="currentColor" /></svg>
</a>
<a class="flex no-underline hover: no-underline text-red-dark bg-blue hover:bg-blue-light text-white font-bold text-lg border-b-4 border-blue-dark hover:border-blue absolute bg-blue-500 rounded-lg px-1 py-1 transition duration-300 ease-in-out hover:bg-blue-600 right-3" href="#">
<span>Select</span>
</a>
</footer>
</article>
Blade template :
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
<!-- Column -->
<div class="my-1 px-1 w-full md:w-1/2 lg:my-4 lg:px-4 lg:w-1/3">
<!-- Template -->
<div id="app">
<buzzevent-template></buzzevent-template>
</div>
<!-- End Template -->
</div>
<!--End Column -->
</div>
</div>
</div>
<script src="{{asset('js/app.js')}}"></script>
</x-app-layout>
If you need any more details please ask.
Output :
Result Image
Looks like the ES6 syntax is a little off, add {} braces into your .then() like:
<script>
import axios from 'axios';
export default {
data(){
return {
Templates:[],
loading:true
}
},
async mounted() {
await axios.post('/getTemplates')
.then(response => { this.Templates = response.data })
.catch(error => { console.log(error.message) })
console.log(this.Templates)
}
}
</script>
Syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Take a look at the following SO question: Issue displaying updated data in Vue component after axios POST
Finally, solved!
Turned out that import (js/app.js) twice. first in the app.blade.php layout template and then in the actual template where i called the component.
That's it.

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 iterable
setPokemon treats pokemon state like it is an array, but pokemon 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 a data key, but accessed as if the data were all at the root level, i.e. element.name.toUpperCase()
minor issues
Use className versus class in JSX
Use camelCased attributes, i.e. strokeWidth versus stroke-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>
);

Categories