Skip to content
Advertisement

Filtering arrays in JS/React

So here I have search functionality. Everything works fine except when an Item has not been found in the array. I have tried something with objects.Keys but it is displaying it on the render not when the Book has not been found like it should. Should I use if statement or.

import React,{useState, useEffect, useRef, useContext}from 'react'
import {FaSearch} from 'react-icons/fa'
import { 
    Link, useHistory
  } from "react-router-dom";
import { BookContext } from '../../context/books';
import SearchBooks from './SearchBooks';

const Search = () => {

  const {data}= useContext(BookContext)
  const [searchValue, setSearchValue] = React.useState('');
  const history= useHistory()
  const ref=useRef()

  function filterBooks(book) {
    console.log(book);
    if (!searchValue.length  ) return false;
    return book.bookName?.toLowerCase().includes(searchValue.toLowerCase());
  }

  const handleSearch = (e) => {
    if (ref.current && !ref.current.contains(e.target)) {
     setSearchValue('')
    }
  };
     
  useEffect(() => {
    document.addEventListener('click', handleSearch);
    return () => {
      document.removeEventListener('click', handleSearch);
    };
  }, []);
  

  
        return (
        <div className='search__cont' ref={ref}>   
              {Object.keys(data).filter(filterBooks).length === 0 &&(
           <div>
             <h3>Book not found</h3>
             </div>
         )}
           <SearchBooks searchValue={searchValue} setSearchValue={setSearchValue }/>          
           {Object.keys(data)
             .map((key) => data[key])
             .reduce((acc, curr) => acc.concat(curr), [])
             .filter(filterBooks)
             .map((book) => {          
             return (
             <>       
              <div  className='search__books'
                onClick={() => {
                  history.push("/book/id", { book }); setSearchValue('')
                }}
               >            
                {" "}
                {book.bookName}{" "}
              </div>            
            </>
          );
        })}
           </div>
    )
}

export default Search

Advertisement

Answer

You’re filtering the category names instead of the books (data is an object with category names as keys and books as values). You can use Object.values and Array.prototype.flat to get an array of all the books and then apply the filter.

const filteredBooks = Object.values(data).flat().filter(filterBooks)
const searchQueryPresent = searchValue.trim().length > 0
{
  searchQueryPresent &&
    (filteredBooks.length === 0 ? (
      <div>No books found</div>
    ) : (
      filteredBooks.map((book) => {
        return <>{/* render books */}</>
      })
    ))
}

Edit on CodeSandbox

Advertisement