Welcome to this step-by-step guide on building a dynamic book search application using React! We'll also dive into some key concepts, best practices, and potential improvements for making the app more robust.
This application allows users to search for books from the Open Library API, displaying results dynamically as they scroll. The app consists of two main parts:
npx create-react-app book-search-app
cd book-search-app
npm install axios
npm start
With the project set up, let's dive into the code.
Let's start by looking at the App.js file, which is the central piece of our application:
import React, { useState, useRef, useCallback } from "react";
import useBookSearch from "./useBookSearch";
import "./App.css";
export default function App() {
const [query, setQuery] = useState("");
const [pageNumber, setPageNumber] = useState(1);
const { books, hasMore, loading, error } = useBookSearch(query, pageNumber);
const observer = useRef();
const lastBookElementRef = useCallback(
(node) => {
if (loading) return;
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
setPageNumber((prevPageNumber) => prevPageNumber + 1);
}
});
if (node) observer.current.observe(node);
},
[loading, hasMore]
);
function handleSearch(e) {
setQuery(e.target.value);
setPageNumber(1);
}
return (
<div className="app-container">
<div className="search-container">
<input
className="search-input"
placeholder="Search Books"
type="search"
value={query}
onChange={handleSearch}
/>
</div>
<div className="books-list">
{loading && <div className="loading-text">Loading...</div>}
{error && <div className="error-text">Error loading books.</div>}
{!loading && books.length === 0 && (
<div className="no-results-text">
{query
? "No results found for this search. Ensure your search is correct "
: "Search results will be displayed here."}
</div>
)}
{books.map((book, index) => (
<div
key={book}
ref={index === books.length - 1 ? lastBookElementRef : null}
className="book-item"
>
{book}
</div>
))}
{!loading && !hasMore && books.length > 0 && (
<div className="no-more-results-text">No more results.</div>
)}
</div>
{/* Footer */}
<footer className="footer">
<p>
©{" "}
<a
href="https://github.com/dennismbugua/Search-Books"
target="_blank"
rel="noopener noreferrer"
>
Source Code
</a>
</p>
</footer>
</div>
);
}
This structure ensures that our application is responsive and efficient, providing a smooth user experience.
Next, let's dive into the useBookSearch.js file, which encapsulates the logic for fetching book data from the Open Library API:
import { useEffect, useState } from "react";
import axios from "axios";
export default function useBookSearch(query, pageNumber) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
const [books, setBooks] = useState([]);
const [hasMore, setHasMore] = useState(false);
useEffect(() => {
setBooks([]);
setLoading(true);
const timer = setTimeout(() => {
fetchData();
}, 0.005);
return () => clearTimeout(timer); // Clear the timer on component unmount or query change
}, [query, pageNumber]);
const fetchData = () => {
setError(false);
let cancel;
axios({
method: "GET",
url: "http://openlibrary.org/search.json",
params: { q: query, page: pageNumber },
cancelToken: new axios.CancelToken((c) => (cancel = c)),
})
.then((res) => {
setBooks((prevBooks) => {
return [
...new Set([...prevBooks, ...res.data.docs.map((b) => b.title)]),
];
});
setHasMore(res.data.docs.length > 0);
setLoading(false);
})
.catch((e) => {
if (axios.isCancel(e)) return;
setError(true);
});
return () => cancel();
};
return { loading, error, books, hasMore };
}
While the current implementation works well, there are several ways to enhance and make the project more robust:
Such dynamic search integrations can be applied in various real-world scenarios:
Building a dynamic book search app with React involves understanding and integrating several key concepts such as state management, custom hooks, and the Intersection Observer API. By following this guide, you should have a solid foundation to create and customize your own search applications.
Feel free to explore and expand upon this example. Happy coding! And don't forget to check out the source code on GitHub for more insights and improvements.
Meet Dennis, a seasoned software engineer with 10 years of experience transforming ideas into digital reality. He has successfully guided countless projects from concept to deployment, bringing innovative solutions to life. With a passion for crafting exceptional software, Dennis has helped countless clients achieve their goals.
Click here to learn more
No popular posts available.
No related posts found.