Bookshelf App Structure
In src folder, we work on App.jsx and components - Header.jsx, BookCard.jsx, BookDetail.jsx, BookList.jsx, SearchBar,jsx, and Footer.jsx as we continue our App development.
Project Setup
Step 1: Initialize the Vite + React Project
Install Vite:
Open your terminal and run:
Copy npm create vite@latest google-bookshelf-app --template react
Navigate to the project folder:
Copy cd google-bookshelf-app
Step 2: Setup File Structure
Create the folders and files as shown in the diagram.
Inside the src
folder, create:
A components
folder containing:
A services
folder containing:
Add App.jsx
, main.jsx
, App.css
, and index.css
. These files are already provided when created the Vite+React project. So you don't need to add this in "src" folder.
Step 3: Confirm the main.jsx
File
The main.jsx
file mounts the React application:
Copy //main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<AppBookShelf />
</React.StrictMode>
);
Step 4: Create the Parent Component AppBookShelf.jsx in src folder
This component will manage the bookshelf functionality.
Copy // src/AppBookShelf.jsx
import React from 'react';
import BookList from './components/BookList';
const App = () => {
return (
<div>
<h1>Google Bookshelf App</h1>
<BookList query="python" />
</div>
);
};
export default App;
Step 5: Create the BookList.jsx
Component in src/components/bookshelf_app
folder
The BookList
component fetches books from the Google Books API using a query passed via props.
Copy // src/components/bookshelp_app/BookList.jsx
import React, { useState, useEffect } from 'react';
const BookList = ({ query }) => {
const [books, setBooks] = useState([]);
const fetchBooks = async () => {
try {
const response = await fetch(`https://www.googleapis.com/books/v1/volumes?q=${query}`);
const data = await response.json();
setBooks(data.items || []);
} catch (error) {
console.error('Error fetching books:', error);
}
};
useEffect(() => {
fetchBooks();
}, [query]);
/*
Book data:
const books = [
{
id: 1,
title: '1984',
author: 'George Orwell',
description: 'A dystopian novel.',
},
{
id: 2,
title: 'The Great Gatsby',
author: 'F. Scott Fitzgerald',
description: 'A novel set in the Jazz Age.',
},
];
*/
return (
<ul>
{books.map((book) => (
<li key={book.id}>
<h2>{book.volumeInfo.title}</h2>
<p>{book.volumeInfo.authors?.join(', ') || 'No Author Info'}</p>
<p>{book.volumeInfo.publisher || 'No Publisher Info'}</p>
</li>
))}
</ul>
);
};
export default BookList;
Step 6: Start the Development Server
Run the following command at vsc terminal to start your Vite development server:
Result
Navigate to http://localhost:5173
to see the "Google Bookshelf App" in action.
The BookList
component dynamically fetches books based on the query prop, which is set to "python"
by default in AppBookshelf.jsx
.
Step-by-Step Procedure to Add NavBar
Component
Step 1. Create the NavBar.jsx
Component :
Inside the components/bookshelf_app
folder, create a new file named NavBar.jsx
.
Add the following content to NavBar.jsx
:
Copy // NavBar.jsx component Layout
NavBar (Component)
├── nav (HTML element - className="navbar")
│ ├── div (className="navbar-left")
│ │ ├── img (src="/logo.png", alt="Logo", className="navbar-logo")
│ │ └── h1 (text="Google Book Shelf")
│ └── div (className="navbar-right")
│ └── SearchBar (Component)
│ └── ... (SearchBar component's internal structure - not shown here)
Copy //NavBar,jsx
import React from 'react';
import SearchBar from './SearchBar'; // Import SearchBar component
import './NavBar.css'; // Optional: Create a CSS file for styling
const NavBar = () => {
return (
<nav className="navbar">
<div className="navbar-left">
<img src="/logo.png" alt="Logo" className="navbar-logo" /> {/* Logo */}
<h1>Google Book Shelf</h1> {/* Heading */}
</div>
<div className="navbar-right">
<SearchBar /> {/* SearchBar component */}
</div>
</nav>
);
};
export default NavBar;
Step 2. Add CSS Styling for NavBar
(Optional) :
Create a new file NavBar.css
in the same directory as NavBar.jsx
and style the navigation bar. Example:
Copy // add the following css code in App.css
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f8f9fa;
padding: 10px 20px;
border-bottom: 1px solid #ddd;
}
.navbar-left {
display: flex;
align-items: center;
}
.navbar-logo {
width: 40px;
height: 40px;
margin-right: 10px;
}
.navbar-right {
display: flex;
align-items: center;
}
Step 3. Update the AppBookShelf.jsx
File :
Modify the AppBookShelf.jsx
file to include the NavBar
component at the top.
Copy // Updated AppBookShelf.jsx
import React from 'react';
import BookList from './components/BookList.jsx';
import NavBar from './components/NavBar.jsx'; // Import NavBar
const App = () => {
return (
<div>
<NavBar /> {/* Add NavBar at the top */}
<BookList />
</div>
);
};
export default App;
Step 4. Update the SearchBar.jsx
File (If Not Already Present) :
Ensure the SearchBar
component is available. Here’s an example SearchBar.jsx
:
Copy // Some code
SearchBar (Component)
├── div (HTML element - className="search-bar")
│ ├── input (HTML element - type="text", placeholder="Search books...", value=query, onChange=function)
│ └── button (HTML element - onClick=handleSearch, text="Search")
Copy // src/components/bookshelf_app/SearchBar.jsx
import React, { useState } from 'react';
const SearchBar = ({ onSearch }) => {
const [query, setQuery] = useState('');
const handleSearch = () => {
if (onSearch) onSearch(query);
};
return (
<div className="search-bar">
<input
type="text"
placeholder="Search books..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button onClick={handleSearch}>Search</button>
</div>
);
};
export default SearchBar;
Step. 5 Add Logo to the Public Folder :
Save your logo file (e.g., logo.png
) in the public/
directory so it can be accessed as /logo.png
.
Step 6. Run Your Project :
Start the development server to verify the integration:
Visit http://localhost:5173
and confirm the NavBar
is displayed with a heading, logo, and search bar.
Completing Other Components
Update SearchBar.jsx component with Form
Add SearchBar.jsx component to pass the "query" to the parent App.jsx
component upon form submission
Step 1: Modify SearchBar.jsx
Make the SearchBar
component receive a callback function (onSearch
) as a prop to pass the search query back to the parent.
Copy // Some code
SearchBar (Component)
├── form (HTML element - onSubmit=handleSubmit, className="search-bar")
│ └── input (HTML element -
type="text", placeholder="Search books...",
value=query, onChange=function, className="form-control me-2")
Copy // src/components/bookshelf_app/SearchBar.jsx
import React, { useState } from 'react';
const SearchBar = ({ onSearch }) => {
const [query, setQuery] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); // Required to Prevent form reload
if (onSearch) onSearch(query); // Call the parent's onSearch function with the query
};
return (
<form className="search-bar" onSubmit={handleSubmit}>
<input
type="text"
placeholder="Search books..."
value={query}
onChange={(e) => setQuery(e.target.value)} // Update query state
/>
<button type="submit">Search</button>
</form>
);
};
export default SearchBar;
Step 2: Modify NavBar.jsx
Pass the onSearch
prop received by NavBar
down to the SearchBar
.
Copy // src/components/bookshelf_app/NavBar.jsx
import React from 'react';
import SearchBar from './SearchBar';
import './NavBar.css';
const NavBar = ({ onSearch }) => {
return (
<nav className="navbar">
<div className="navbar-left">
<img src="/logo.png" alt="Logo" className="navbar-logo" />
<h1>Google Book Shelf</h1>
</div>
<div className="navbar-right">
<SearchBar onSearch={onSearch} /> {/* Pass onSearch prop to SearchBar */}
</div>
</nav>
);
};
export default NavBar;
Step 3: Modify BookList.jsx
Update BookList
to receive the query
as a prop and fetch books based on the updated query.
Copy // src/components/bookshelf_app/BookList.jsx
import React, { useState, useEffect } from 'react';
const BookList = ({ query }) => {
const [books, setBooks] = useState([]);
const fetchBooks = async () => {
try {
const response = await fetch(`https://www.googleapis.com/books/v1/volumes?q=${query}`);
const data = await response.json();
setBooks(data.items || []);
} catch (error) {
console.error('Error fetching books:', error);
}
};
useEffect(() => {
fetchBooks();
}, [query]); // Refetch books when query changes
return (
<ul>
{books.map((book) => (
<li key={book.id}>
<h2>{book.volumeInfo.title}</h2>
<p>{book.volumeInfo.authors?.join(', ') || 'No Author Info'}</p>
<p>{book.volumeInfo.publisher || 'No Publisher Info'}</p>
</li>
))}
</ul>
);
};
export default BookList;
Step 4: Modify the parent component AppBookShelf.jsx
In AppBookShelf.jsx
, manage the query
state and pass the onSearch
handler to NavBar
. Update BookList
to use the dynamic query.
Copy // src/AppBookShelf.jsx
import React, { useState } from 'react';
import NavBar from './components/NavBar';
import BookList from './components/BookList';
const App = () => {
const [query, setQuery] = useState('python'); // Default query
const handleSearch = (newQuery) => {
setQuery(newQuery); // Update query when a new search is performed
};
return (
<div>
<NavBar onSearch={handleSearch} /> {/* Pass handleSearch to NavBar */}
<BookList query={query} /> {/* Pass query to BookList */}
</div>
);
};
export default App;
Step 5: Test the Application
Run the development server:
Open the app in the browser.
Use the search bar to input a new query and submit the form. Verify that:
The BookList
component updates with books matching the new query.
The query state is correctly managed in AppBookshelf
.
Summary of Changes
SearchBar.jsx
: Added a form submission handler to call onSearch
with the query.
NavBar.jsx
: Passed the onSearch
prop to SearchBar
.
BookList.jsx
: Updated to dynamically fetch books based on the query
prop.
AppBookShelf.jsx
: Managed the query
state and passed it to both NavBar
and BookList
.