Lesson 5 - Create BookList.jsx Component
Creating BookList.jsx Component
Next, write a React child component code named "BookList.jsx" to display book contents with fetched data and query.
Layout Structure
// Layout Design of BookList Component
BookList Component
│
├── Props
│ ├── books (array) - Collection of book objects
│ └── query (string) - Current search query
│
└── Render Structure
└── <div> (mt-4)
├── <h3> (text-center) - "Book Search Results for '{query}'"
│
└── Conditional Rendering
├── IF books.length > 0
│ └── <div> (row)
│ └── books.map() - Iterates through each book
│ └── <div> (col-md-4 mb-4) - For each book
│ └── <div> (card h-100)
│ └── <div> (card-body)
│ ├── <h5> (card-title) - Book title
│ │
│ ├── <p> (card-text) - Authors
│ │ ├── <strong>Authors:</strong>
│ │ └── Conditional: authors.join(', ') OR 'Unknown'
│ │
│ ├── <p> (card-text) - Description
│ │ ├── <strong>Description:</strong>
│ │ └── Conditional: truncated description OR 'No Description Available'
│ │
│ └── <p> (card-text) - Publisher
│ ├── <strong>Publisher:</strong>
│ └── Conditional: publisher OR 'Unknown'
│
└── ELSE
└── <p> (text-center) - "No books found for '{query}'"
// BookList.jsx
// Import necessary libraries
import React from 'react';
import PropTypes from 'prop-types';
const BookList = ({ books, query }) => {
return (
<div className="mt-4">
{/* Heading to show the current search query */}
<h3 className="text-center">Book Search Results for "{query}"</h3>
{/* Check if books are available */}
{books.length > 0 ? (
<div className="row">
{books.map((book) => (
<div key={book.id} className="col-md-4 mb-4">
<div className="card h-100">
<div className="card-body">
{/* Display book title */}
<h5 className="card-title">{book.volumeInfo.title || 'No Title Available'}</h5>
{/* Display book authors */}
<p className="card-text">
<strong>Authors:</strong>{' '}
{book.volumeInfo.authors ? book.volumeInfo.authors.join(', ') : 'Unknown'}
</p>
{/* Display book description */}
<p className="card-text">
<strong>Description:</strong>{' '}
{book.volumeInfo.description
? book.volumeInfo.description.substring(0, 100) + '...'
: ('No Description Available')}
</p>
{/* Display book publisher */}
<p className="card-text">
<strong>Publisher:</strong>{' '}
{book.volumeInfo.publisher || 'Unknown'}
</p>
</div>
</div>
</div>
))}
</div>
) : (
<p className="text-center">No books found for "{query}"</p>
)}
</div>
);
};
// Define PropTypes for the component
BookList.propTypes = {
books: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
volumeInfo: PropTypes.shape({
title: PropTypes.string,
authors: PropTypes.arrayOf(PropTypes.string),
description: PropTypes.string,
publisher: PropTypes.string,
}).isRequired,
})
).isRequired,
query: PropTypes.string.isRequired,
};
export default BookList;
Explanation of BookList.jsx
Code Structure
BookList.jsx
Code StructureThe BookList
component is designed to display a list of books fetched from the API along with the query used. Here's the step-by-step explanation:
1. Importing Libraries
import React from 'react';
import PropTypes from 'prop-types';
React
: Core library for creating React components.PropTypes
: Ensures the props passed to the component are valid and well-defined.Bootstrap: Provides styling for the layout and components.
2. Component Declaration
const BookList = ({ books, query }) => {
Props:
books
: An array of book objects fetched from the API.query
: The current search term entered by the user.
3. Display Book Query Search Results
<h3 className="text-center">Results for "{query}"</h3>
Purpose:
Displays the current search query to inform the user of the context.
4. Conditional Rendering
{books.length > 0 ? (
// Render book cards if books are available
) : (
<p className="text-center">No books found for "{query}"</p>
)}
If books exist:
Render a list of book cards dynamically using
map
.
If no books are found:
Show a user-friendly message.
5. Rendering Book Information
<div className="row">
{books.map((book) => (
<div key={book.id} className="col-md-4 mb-4">
<div className="card h-100">
<div className="card-body">
<h5 className="card-title">{book.volumeInfo.title || 'No Title Available'}</h5>
<p className="card-text"><strong>Authors:</strong> {book.volumeInfo.authors ? book.volumeInfo.authors.join(', ') : 'Unknown'}</p>
<p className="card-text"><strong>Description:</strong> {book.volumeInfo.description ? book.volumeInfo.description.substring(0, 100) + '...' : 'No Description Available'}</p>
<p className="card-text"><strong>Publisher:</strong> {book.volumeInfo.publisher || 'Unknown'}</p>
</div>
</div>
</div>
))}
</div>
map
Function:Iterates over the
books
array to dynamically generate a Bootstrap card for each book.
Book Details:
Title: Displays the book's title or a fallback message.
Authors: Joins the list of authors or displays
"Unknown"
.Description: Shows the first 100 characters of the description with a fallback.
Publisher: Displays the publisher or
"Unknown"
.Search Result Style: modify
col-md-6
6. Prop Validation
BookList.propTypes = {
books: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
volumeInfo: PropTypes.shape({
title: PropTypes.string,
authors: PropTypes.arrayOf(PropTypes.string),
description: PropTypes.string,
publisher: PropTypes.string,
}).isRequired,
})
).isRequired,
query: PropTypes.string.isRequired,
};
Purpose:
Validates the structure and types of
books
andquery
props to prevent runtime errors.
Details:
books
is an array of objects with a defined structure.query
is a string and must be provided.
7. Exporting the Component
export default BookList;
Purpose:
Makes the
BookList
component reusable in other parts of the app.
Key Takeaways for Students:
Dynamic Rendering:
Use the
map
function to generate a list of items dynamically.Handle missing or undefined data gracefully with fallback values.
Conditional Logic:
Use conditional rendering to display appropriate content when data is unavailable.
Prop Validation:
Use
PropTypes
to ensure data passed to components meets the expected format.
Styling with Bootstrap:
Quickly style components using Bootstrap classes for layout and spacing.
This component is ready to integrate into the parent AppBookShelf.jsx
. Let me know if you need further explanation or want to proceed with the next child component!
Challenge :
Add "Read More" button to get the full description about the book when clicked by creating a new component "ReadMore.jsx"
Implement a react child component named "ReadMore.jsx" to show the full description of the book selected when clicked "Read More" button and when clicked "Close" button, go back to the previous state.
// Code Snippet in BookList.jsx
{/* Display book description */}
<p className="card-text">
<strong>Description:</strong>{' '}
{book.volumeInfo.description ? (
<ReadMore text={book.volumeInfo.description} />
) : (
'No Description Available'
)}
</p>
ReadMore Component
// Components/ReadMore.jsx
// Import necessary libraries
import React, { useState } from 'react'
import PropTypes from 'prop-types'
const ReadMore = ({ text }) => {
const [isExpanded, setIsExpanded] = useState(false)
// Toggle the expanded state
const toggleExpanded = () => {
setIsExpanded(!isExpanded)
}
return (
<div>
{isExpanded ? (
<>
<p>{text}</p>
<button className="btn btn-secondary btn-sm" onClick={toggleExpanded}>
Close
</button>
</>
) : (
<>
<p>{text.length > 100 ? `${text.substring(0, 100)}...` : text}</p>
{text.length > 100 && (
<button className="btn btn-primary btn-sm" onClick={toggleExpanded}>
Read More
</button>
)}
</>
)}
</div>
)
}
// Define PropTypes for the component
ReadMore.propTypes = {
text: PropTypes.string.isRequired,
}
export default ReadMore
Last updated