I want to have a double search feature in my app. One search, filters books out by its title. While the other, filters books by genre. Both search filters should work together (it should filter out by title AND genre if search terms are provided).
I'm having difficulty applying the genre search filter, however.
** NOTE**: When created, each genre tag is assigned the same id as the book it's tagged to. This way, I can pull up all the books associated with a genre that's being search.
Objects:
book = { id: "id", title: "title", author: "author"}
genre = { id: "id", label: "label" }
Parent component
export default function Books() {
const [books, setBooks] = useState([])
const [searchTitle, setSearchTitle] = useState("")
const [genres, setGenres] = useState([])
const [searchGenre, setSearchGenre] = useState("")
useEffect(() => {
fetch(`https://www.someapi.com/books`)
.then(r => r.json())
.then(setBooks)
}, [])
function handleAllGenres(val){
setGenres([...genres, val])
}
const filterGenres = genres.filter(genre => {
return genre.label.toLowerCase().includes(searchGenre.toLowerCase())
})
const filterBooks = books.filter(book => {
return book.title.toLowerCase().includes(searchTitle.toLowerCase())
})
const filterBookGenres = filterBooks.filter(book => {
if (searchGenres.length > 0) {
return filterGenres.some(genre => book.id === genre.id)
} else {
return books
}
})
const book = filterBookGenres.map(book => {
return (
<Book
key={book.id}
book={book}
genres={genres}
onAddGenre={handleAllGenres}
/>
)
})
return (
<div className="main-container">
<SearchTitle
searchTitle={searchTitle}
onSearchTitle={setSearchTitle}
/>
<SearchGenre
searchGenre={searchGenre}
onSearchGenre={setSearchGenre}
/>
{book}
</div>
);
}
Child Component:
export default function Book({ book, onAddGenre })
const [genreTags, setGenreTags] = useState([])
function handleAddGenre(e) {
const val = {
id: book.id,
label: e.target.value,
}
if (e.key === 'Enter' && val) {
if (genreTags.find(genre => genre.toLowerCase() === val.toLowerCase())) {
return
}
setGenreTags([...genreTags, val])
onAddGenre(val)
e.target.value = ""
}
}
return (
<div className="book-div">
<p>{title}</p>
<p>Written By: {author}</p>
</div>
<div className="tag-div">
<ul>
{ genreTags && genreTags.map(tag => {
return <li key={uuid()}> {tag.label} </li>
})}
</ul>
</div>
<input
type="text"
placeholder="Add a genre"
onKeyDown={handleAddGenre}
/>
);
}
Search Components:
export default function SearchTitle({ searchTitle, onSearchTitle }) {
return (
<div className="search">
<input
id="name-input"
placeholder="Search by title"
value={search}
onChange={e => onSearch(e.target.value)}
/>
</div>
);
}
export default function SearchGenre({ searchGenre, onSearchGenre }) {
return (
<div className="search">
<input
id="tag-input"
placeholder="Search by genre"
value={searchGenre}
onChange={e => onSearchTag(e.target.value)}
/>
</div>
)
}
UPDATE: The search features work now, but I noticed that my tags disappear after I play around with either search features. For example, if I input a search value and filter through the books list then delete my search terms, my tags will disappear. I assume it's because the books get re-rendered?
Is there a way to prevent this re-render or to prevent my tags from disappearing after playing around with my search feature?
All the help appreciated. Thank you!