Search Books

import { navLinks } from "../../../data/links"
import LogoImg from "../../../assets/images/logo.png"
import { Link } from "react-router-dom"


import { useState } from "react";
import { useNavigate } from "react-router-dom";


function Navbar() {
    const [query, setQuery] = useState("");
    const navigate = useNavigate();


    const handleSubmit = (e) => {
  e.preventDefault();

  const cleanQuery = query.trim();

  if (!cleanQuery) return;

  navigate(`/search?q=${cleanQuery}`);
};



    return (
        <div className="shadow-sm">
            <nav className="navbar navbar-expand-lg bg-body-tertiary">
                <div className="container">
                    <a className="navbar-brand" href="/">
                        <img src={LogoImg} alt="logo" width="40px" />
                    </a>
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>
                    <div className="collapse navbar-collapse" id="navbarSupportedContent">
                        <ul className="navbar-nav me-auto mb-2 mb-lg-0">

                            {
                                navLinks.map((link) => {
                                    return (
                                        <li key={link.id} className="nav-item">
                                            <Link className="nav-link" to={link.link}>{link.title}</Link>
                                        </li>
                                    )
                                })
                            }

                        </ul>
                        {/* <form className="d-flex" role="search">
                            <input className="form-control me-2" type="search" placeholder="إبحث" aria-label="Search" />
                            <button className="btn btn-outline-success" type="submit">إبحث</button>
                        </form> */}

                        <form onSubmit={handleSubmit} className="d-flex">
                            <input
                                type="text"
                                className="form-control"
                                placeholder="ابحث عن كتاب..."
                                value={query}
                                onChange={(e) => setQuery(e.target.value)}
                            />
                            <button className="btn btn-primary ms-2">
                                بحث
                            </button>

                        </form>
                    </div>
                </div>
            </nav>
        </div>
    )
}

export default Navbar
JavaScript
import { useSearchParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { useMemo } from "react";
import Book from "./Book/Book";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { getAllBooks } from "../store/booksSlice";



const Search = () => {

    const dispatch = useDispatch();

  const [searchParams] = useSearchParams();

 const query = searchParams.get("q")?.toLowerCase().trim() || "";

  const {data , isLoading , error} = useSelector((state)=> state.books);

  const filteredBooks = useMemo(() => {

    return data.filter(book =>
      book.name.toLowerCase().includes(query)

    );

  }, [data, query]);

  useEffect(() => {
  if (data.length === 0) {
    dispatch(getAllBooks());
  }
}, [data.length, dispatch]);

  return (
    <div className="container mt-4">

      <h3>نتائج البحث عن: "{query}"</h3>

      {filteredBooks.length === 0 && (
        <p>لا توجد نتائج</p>
      )}

      <div className="row row-cols-lg-5">
        {filteredBooks.map(book => (
            <Book book={book}/>
        ))}
      </div>

    </div>
  );
};

export default Search;
JavaScript

الطريقة الثانية:

أولًا: تنفيذ Live Search بدون Debounce (للفهم)

import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";

const NavbarSearch = () => {

  const [query, setQuery] = useState("");
  const navigate = useNavigate();

  useEffect(() => {

    const cleanQuery = query.trim();

    if (cleanQuery) {
      navigate(`/search?q=${cleanQuery}`);
    }

  }, [query, navigate]);

  return (
    <input
      type="text"
      className="form-control"
      placeholder="ابحث..."
      value={query}
      onChange={(e) => setQuery(e.target.value)}
    />
  );
};
JavaScript

التنفيذ الصحيح باستخدام setTimeout

import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";

const NavbarSearch = () => {

  const [query, setQuery] = useState("");
  const navigate = useNavigate();

  useEffect(() => {

    const timeout = setTimeout(() => {

      const cleanQuery = query.trim();

      if (cleanQuery) {
        navigate(`/search?q=${cleanQuery}`);
      }

    }, 400); // الانتظار 400ms

    return () => clearTimeout(timeout);

  }, [query, navigate]);

  return (
    <input
      type="text"
      className="form-control"
      placeholder="ابحث..."
      value={query}
      onChange={(e) => setQuery(e.target.value)}
    />
  );
};

export default NavbarSearch;
JavaScript