I'm using the Jikan API to create an anime gallery of some sort, one of the features I wanted to include was to be able to search for a specific anime title within the gallery. My issue is that with my current search input field; despite specifying a show, it will show the specific title you searched for along with a couple of duplicated titles of an entirely different show.
Another issue I'm having is that despite setting a limit to how many shows data will be fetched, once I clear the search field, those same duplicates will be added onto the gallery despite the limitation.
App.js
import './App.css';
import AnimeGallery from './components/AnimeGallery';
import Footer from './components/Footer';
import Header from './components/Header';
import NavFilter from './components/NavFilter';
import { useState, useEffect } from 'react';
function App() {
const animeApi = 'https://api.jikan.moe/v4/top/anime?type=tv&sfw=true&limit=12&filter=airing';
const [animeList, setAnimeList] = useState([]);
const [filteredAnime, setFilteredAnime] = useState([])
useEffect(() => {
const fetchAnimeGenre = async () => {
const result = await fetch(animeApi);
const data = await result.json();
setAnimeList(data.data);
setFilteredAnime(data.data);
};
fetchAnimeGenre();
}, []);
return (
<>
<Header />
<NavFilter animeList={animeList} setFilteredAnime={setFilteredAnime} />
<AnimeGallery animeList={filteredAnime} />
<Footer />
</>
);
}
export default App;
NavFilter.js
import '../styles/NavFilter.module.css'
import { useState } from 'react';
const NavFilter = ({ animeList, setFilteredAnime }) => {
const [searchAnime, setSearchAnime] = useState('');
const preventReload = (e) => {
e.preventDefault();
}
const handleSearchNav = (e) => {
const searchTitle = e.target.value;
setSearchAnime(searchTitle);
if (searchTitle === '') {
setFilteredAnime(animeList);
} else {
const filteredResults = animeList.filter(anime =>
anime.title.toLowerCase().includes(searchTitle.toLowerCase())
);
setFilteredAnime(filteredResults);
};
}
return (
<>
<nav>
<div className="w-full p-5 shadow-xl">
<ul className="flex justify-center tracking-wider text-sm">
<li>
<form
onSubmit={preventReload}
>
<input
type="text"
className="rounded-md px-8 py-1"
placeholder="Search Anime..."
value={searchAnime}
onChange={handleSearchNav}></input>
</form>
</li>
</ul>
</div>
</nav>
</>
)
}
export default NavFilter;
AnimeGallery.js
import '../styles/AnimeGallery.module.css';
const AnimeGallery = ({ animeList }) => {
return (
<>
<section className="anime-gallery container mx-auto grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 px-3 py-3 mt-20">
{animeList.map((anime, index) => (
<article className="anime-card flex mx-3 my-2 rounded-lg h-72 bg-gray-dark shadow-md hover:-translate-y-3" key={anime.mal_id}>
<img className="anime-card-poster h-72" src={anime.images.jpg.image_url} alt={anime.title} />
<div className="anime-details p-3 w-full flex flex-col overflow-ellipsis overflow-auto">
<h3 className="anime-card-title text-xl tracking-wide text-balance text-baby-blue font-bold"> {anime.title}</h3>
<p className="anime-card-synopsis text-sm mt-2 text-gray-light"> Episodes: {anime.episodes || 'N/A'}</p>
<p className="anime-card-rating text-sm text-gray-light"> Rating: {anime.score}</p>
<p className="anime-card-status text-sm text-gray-light"> Status: {anime.status}</p>
<p className="anime-genres text-sm text-gray-light flex flex-wrap">
{anime.genres.map((genre) => (
<p key={genre.mal_id} className="genre-btn px-2 bg-seafoam ms-1 rounded-lg mb-1 mt-2">{genre.name}</p>
))}
</p>
</div>
</article>
))}
</section>
</>
)
}
export default AnimeGallery;
For the duplicated titles I did try setting a key to equal the specific id of the show from the API but the duplicates are still present, and I'd also tried using a reduce method in place of my filter.
I also tried utilizing derived state and kept my state variable animeList
as the only and entire list of the anime then one other state just for my search query so I could set the filter and specifications within my render, I didn't notice any changes with that approach either, but to be fair, I had to do a lot of research on the concept of derived state so perhaps I didn't implement it well.
There is a duplicate mal_id
in the API response, hence rendering a list with that as a key causes problems while updating the list.
Try creating your own ids like below.
useEffect(() => {
const fetchAnimeGenre = async () => {
const result = await fetch(animeApi);
const data = await result.json();
const transformedData = data.data.map((obj) => ({
...obj,
id: crypto.randomUUID()
}));
setAnimeList(transformedData);
setFilteredAnime(transformedData);
};
fetchAnimeGenre();
}, []);
Make sure you also use this new id as the key in the list. key={anime.id}