Tạo Wrapper Component trong React với props
- 26-09-2022
- Toanngo92
- 0 Comments
Để hiểu bài viết này, hãy xem kĩ lại bài viết giới thiệu props trong ReactJS và tùy chỉnh thành phần (component) với props trước để đảm bảo bạn đã hiểu rõ về component. Trong bài viết này, chúng ta sẽ vẫn tiếp tục sử dụng dữ liệu mẫu và code mẫu của bài trước để làm ví dụ.
Mục lục
Tạo Wrapper Component
Nội dung file App.js
import './App.css';
import data from './data/data';
import ProductComp from './components/product/ProductComponent';
const App = () => {
return (
<div className='container'>
<table>
<thead>
<tr>
<th>ID</th>
<th>Product</th>
<th>Type</th>
<th>PPU</th>
</tr>
</thead>
<tbody>
{data.map((item) => {
return (<ProductComp key={item.id} id={item.id} name={item.name} type={item.type} ppu={item.ppu} batters={item.batters} topping={item.topping} />)
})}
</tbody>
</table>
</div>
)
}
export default App;
Vẫn sử dụng file theo cấu trúc data/data.js như sau:
const data = [
{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
},
{
"id": "0002",
"type": "donut",
"name": "Raised",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
},
{
"id": "0003",
"type": "donut",
"name": "Old Fashioned",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}
];
export default data
Nội dung file components/product/ProductComp.js như sau:
import PropTypes from 'prop-types'
console.log(PropTypes);
const ProductComp = ({id,name,type,ppu,batters,topping}) => {
const showBatters = (event,batters) =>{
let batterstr = '';
batters.batter.forEach(((item) => {
batterstr += item.type+ '\n';
}));
alert(batterstr);
}
const showTopping = (event,topping) =>{
let toppingstr = '';
topping.forEach(((item) => {
toppingstr += item.type+ '\n';
}));
alert(toppingstr);
}
return (<tr>
<td>{id}</td>
<td>{name}</td>
<td>{type}</td>
<td>{ppu}</td>
<td><button onClick={(event) => showBatters(event,batters)}>Show batters</button></td>
<td><button onClick={(event) => showTopping(event,topping)}>Show topping</button></td>
</tr>)
}
export default ProductComp
ProductComp.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
ppu: PropTypes.number.isRequired,
// batters: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
batters: PropTypes.shape(
{
batter : PropTypes.arrayOf(PropTypes.object)
}
),
topping: PropTypes.arrayOf(PropTypes.object),
showBatters: PropTypes.func,
showTopping: PropTypes.func
}
Khi bạn làm việc trên các dự án phức tạp hơn, dữ liệu của bạn sẽ đến từ nhiều nơi khác nhau hơn, chẳng hạn như API, localStorage hoặc tệp tĩnh. Tuy nhiên, quy trình sử dụng từng biến này sẽ tương tự nhau: gán dữ liệu cho một biến và lặp qua dữ liệu. Trong trường hợp này, dữ liệu là từ một tệp tĩnh, vì vậy bạn đang nhập trực tiếp vào một biến.
Trong đoạn mã này, bạn sử dụng phương thức .map () để lặp lại các ProductComp và hiển thị các prop. Lưu ý rằng bạn không phải sử dụng từng phần dữ liệu. Bạn cũng đang thêm một key riêng mà React sẽ sử dụng để theo dõi dữ liệu được ánh xạ. Cuối cùng, bạn đang bao bọc mã bằng một thẻ <tbody/>.
Ở ví dụ trên, chúng ta hiển thị nút và sẽ mở dialog box biểu diễn các dữ liệu phức tạp của ProductComp thông qua sự kiện onClick, bây giờ mình không muốn như vậy nữa mà sẽ hiển thị thẳng ra bên trong thẻ <td> đang bao bọc, vì vậy mình sẽ bắt đầu thao tác các bước như sau:
Tạo file components/product/BatterComponent.js:
import React from "react";
import PropTypes from 'prop-types'
const BatterComp = (batters) => {
const batter = batters.batters.batter // array
return (<ul>
{batter.map((item) => {
return <li key={item.id}><strong>Id:</strong><i>{item.id}</i><strong>Type</strong><i>{item.type}</i></li>
})}
</ul>)
}
export default BatterComp
BatterComp.propTypes = {
batters: PropTypes.object
}
BatterComp.defaultProps = {
}
Tạo file components/product/ToppingComponent.js:
import React from "react";
import PropTypes from 'prop-types'
const ToppingComp = (topping) => {
const toppingarr = topping.topping
return (<ul>{
toppingarr.map((item) => {
return <li key={item.id}><strong>Id:</strong><i>{item.id}</i><strong>Type</strong><i>{item.type}</i></li>
})
}</ul>)
}
export default ToppingComp
ToppingComp.propTypes = {
topping: PropTypes.array
}
ToppingComp.defaultProps = {
}
Sửa file components/product/ProductComponent.js:
import React from "react";
import PropTypes from 'prop-types'
import BatterComp from "./BatterComponent";
import ToppingComp from "./ToppingComponent";
const ProductComp = ({id,name,type,ppu,batters,topping}) => {
const showTopping = (event,topping) =>{
let toppingstr = '';
topping.forEach(((item) => {
toppingstr += item.type+ '\n';
}));
alert(toppingstr);
}
return (<tr>
<td>{id}</td>
<td>{name}</td>
<td>{type}</td>
<td>{ppu}</td>
<td><BatterComp batters={batters} /></td>
<td><ToppingComp topping={topping} /></td>
</tr>)
}
export default ProductComp
ProductComp.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
ppu: PropTypes.number.isRequired,
// batters: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
batters: PropTypes.shape(
{
batter : PropTypes.arrayOf(PropTypes.object)
}
),
topping: PropTypes.arrayOf(PropTypes.object),
showBatters: PropTypes.func,
showTopping: PropTypes.func
}
ProductComp.defaultProps = {
ppu: 0
}
Cấu trúc dự án và output:
Truyền dữ liệu (pass data) thông qua props
Chuyển thông tin chi tiết qua một thành phần với … prop
Các thành phần đang hoạt động tốt với nhau, nhưng có một chút kém hiệu quả trong ProductComp. Bạn đang lấy các thuộc tính batter và topping thông qua dữ liệu mẫu, nhưng bạn không sử dụng dữ liệu. Thay vào đó, bạn đang chuyển nó qua component. Điều này vốn dĩ không có gì sai cả — trên thực tế, tốt vấn đề là bạn sẽ truyền dữ liệu qua nhiều bước, khi làm điều này, bạn làm cho mã của mình khó bảo trì hơn. Bất cứ khi nào bạn muốn Truyền dữ liệu mới vào BatterComponent hoặc ToppingComponent, bạn cần cập nhật ba vị trí: App.js, nơi bạn lấy dữ liệu của dữ liệu mẫu chuyển các props, BatterComponent hoặc ToppingComponent, nơi sử dụng props và ProductComp, là nơi giống như nơi trung chuyển nằm giữa giữa.
Giải pháp tốt hơn là nên thu thập bất kỳ prop nào không sử dụng bên trong ProductComp và sau đó chuyển trực tiếp chúng đến BatterComponent hoặc ToppingComponent. Điều này cho bạn cơ hội thực hiện thay đổi đối với BatterComponent hoặc ToppingComponent mà không cần thay đổi thẻ ProductComp. Trên thực tế, ProductComp không cần biết bất kỳ điều gì về các props hoặc các PropTypes sẽ được đưa vào BatterComponent hoặc ToppingComponent.
Để làm điều đó, bạn sẽ sử dụng toán tử phần còn lại đối tượng. Toán tử này thu thập bất kỳ mục nào không được lấy ra trong quá trình destructure và lưu chúng vào một đối tượng mới.
Đây là một ví dụ đơn giản:
const topping = {
"name": "topping",
"toppingval" : [
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}
const {name, ...props} = topping;
console.log(name)
console.log(props)
Với ví dụ trên, dữ liệu được log ra của bến name sẽ có giá trị là:
topping
Dữ liệu biến props có giá trị là:
{ "toppingval" : [
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
] }
Áp dụng vào trường hợp phía trên như sau:
Sửa file components/product/ProductComponent.js
import React from "react";
import PropTypes from 'prop-types'
import BatterComp from "./BatterComponent";
import ToppingComp from "./ToppingComponent";
const ProductComp = ({id,name,type,ppu,...props}) => {
return (<tr>
<td>{id}</td>
<td>{name}</td>
<td>{type}</td>
<td>{ppu}</td>
<td><BatterComp {...props} /></td>
<td><ToppingComp {...props} /></td>
</tr>)
}
export default ProductComp
ProductComp.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
ppu: PropTypes.number.isRequired,
// batters: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
batters: PropTypes.shape(
{
batter : PropTypes.arrayOf(PropTypes.object)
}
),
topping: PropTypes.arrayOf(PropTypes.object),
showBatters: PropTypes.func,
showTopping: PropTypes.func
}
ProductComp.defaultProps = {
ppu: 0
}
Nếu muốn cấu trúc code rõ ràng hơn, có thể sửa biến topping và batters file BatterComponent hoặc ToppingComponent thành props, nếu muốn code rõ ràng hơn nữa, chúng ta có thể sử dụng destructure object trong biến hứng props từ BatterComponent và ToppingComponent, ví dụ với ToppingComponent
import React from "react";
import PropTypes from 'prop-types'
const ToppingComp = ({batters,topping}) => {
const toppingarr = topping
return (<ul>{
toppingarr.map((item) => {
return <li key={item.id}><strong>Id:</strong><i>{item.id}</i><strong>Type</strong><i>{item.type}</i></li>
})
}</ul>)
}
export default ToppingComp
ToppingComp.propTypes = {
topping: PropTypes.array
}
ToppingComp.defaultProps = {
}
Trong bước này, bạn đã học cách tạo các wrapper component linh hoạt có thể lấy các prop không xác định và chuyển chúng vào các component lồng nhau bằng toán tử spread. Đây là một mẫu phổ biến sẽ cung cấp cho bạn sự linh hoạt cần thiết để tạo các component có trách nhiệm tập trung. Trong bước tiếp theo, chúng ta sẽ tạo các component có thể lấy các thành phần không xác định làm chỗ dựa bằng cách sử dụng khái niệm children prop được tích hợp sẵn.
Tạo Wrapper Component với children
Trong bước này, bạn sẽ tạo một wrapper component có thể lấy một nhóm component không xác định làm prop. Điều này sẽ cung cấp cho bạn khả năng lồng ghép các thành phần như HTML tiêu chuẩn và nó sẽ cung cấp cho bạn một mẫu để tạo các trình bao bọc có thể tái sử dụng, cho phép bạn tạo nhiều component khác nhau cần thiết kế chung nhưng cơ chế biểu diễn linh hoạt.
React cung cấp cho bạn một builtin prop có tên là child để thu thập bất kỳ component con nào. Sử dụng điều này làm cho việc tạo các component của trình bao bọc trở nên trực quan và dễ đọc.
Để bắt đầu, hãy tạo một thành phần mới có tên là ProductCompWrapper. Đây sẽ là một trình bao bọc
component để tạo ra một phong cách tiêu chuẩn cho bất kỳ ProductComponent mới nào.
Tạo file components/product/ProductCompWrapper.js
import React from "react";
import PropTypes from 'prop-types'
const ProductComWrapper = ({ children, title }) => {
return (
<tr title={title}>{children}</tr>
)
}
export default ProductComWrapper
ProductComWrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
title: PropTypes.string.isRequired,
}
Không giống như một React Component, bạn không cần phải có một root Component duy nhất khi sử dụng child. Đó là lý do tại sao PropType cho ProductComWrapper nó có thể là một mảng các Component hoặc một Component đơn. Ngoài việc chuyển các child dưới dạng các component lồng nhau, bạn đang đặt cho ProductComWrapper một title.
Sửa file components/product/ProductComponent.js
import React from "react";
import PropTypes from 'prop-types'
import BatterComp from "./BatterComponent";
import ToppingComp from "./ToppingComponent";
const ProductComp = ({id,name,type,ppu,...props}) => {
return (<>
<td>{id}</td>
<td>{name}</td>
<td>{type}</td>
<td>{ppu}</td>
<td><BatterComp {...props} /></td>
<td><ToppingComp {...props} /></td>
</>)
}
export default ProductComp
ProductComp.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
ppu: PropTypes.number.isRequired,
// batters: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
batters: PropTypes.shape(
{
batter : PropTypes.arrayOf(PropTypes.object)
}
),
topping: PropTypes.arrayOf(PropTypes.object),
showBatters: PropTypes.func,
showTopping: PropTypes.func
}
ProductComp.defaultProps = {
ppu: 0
}
Sửa file App.js
import './App.css';
import data from './data/data';
import ProductComp from './components/product/ProductComponent';
import ProductComWrapper from './components/product/ProductCompWrapper';
const App = () => {
return (
<div className='container'>
<table>
<thead>
<tr>
<th>ID</th>
<th>Product</th>
<th>Type</th>
<th>PPU</th>
<th>Batters</th>
<th>Topping</th>
</tr>
</thead>
<tbody>
{data.map((item) => {
return (<ProductComWrapper key={item.id} title="helleo"><ProductComp id={item.id} name={item.name} type={item.type} ppu={item.ppu} batters={item.batters} topping={item.topping} /></ProductComWrapper>)
})}
</tbody>
</table>
</div>
)
}
export default App;
Bây giờ bạn có một component ProductComWrapper có thể sử dụng lại có thể lấy bất kỳ số lượng con lồng nhau nào. Ưu điểm chính của việc này là bạn có thể sử dụng lại ProductComWrapper với bất kỳ component tùy ý nào. Nếu bạn muốn tạo thẻ mô tả một Product với kiểu dữ liệu khác, bạn có thể làm điều đó bằng cách gói thông tin dữ liệu với component ProductComWrapper .
Nó thậm chí không cần phải liên quan gì cả: Nếu bạn muốn sử dụng lại thành phần ProductComWrapper trong một ứng dụng hoàn toàn khác liệt kê những thứ như âm nhạc hoặc dữ liệu tài khoản, bạn cũng có thể làm điều đó. Thành phần ProductComWrapper không quan tâm child là gì; bạn chỉ đang sử dụng lại phần tử wrapper.
Nhược điểm của việc sử dụng child là bạn chỉ có thể có một instance của child. Đôi khi, bạn sẽ muốn một thành phần có JSX tùy chỉnh ở nhiều nơi. May mắn thay, bạn có thể làm điều đó bằng cách chuyển các JSX và component React như là prop, mà chúng ta sẽ đề cập trong bước tiếp theo.
Truyền Component như là Props
Trong bước này, bạn sẽ sửa đổi component ProductComWrapper để lấy các component khác làm prop. Điều này sẽ cung cấp cho component của bạn sự linh hoạt tối đa để hiển thị các thành phần không xác định hoặc JSX ở nhiều vị trí trên toàn bộ trang.
Không giống như child, bạn chỉ có thể sử dụng một lần, bạn có thể có nhiều các component làm prop, mang lại cho wrapper component khả năng thích ứng với nhiều nhu cầu khác nhau trong khi vẫn duy trì giao diện và cấu trúc chuẩn.
Đến cuối bước này, bạn sẽ có một component có thể bao bọc các thành phần con và cũng hiển thị các thành phần khác trong ProductComWrapper . Mẫu này sẽ cung cấp cho bạn sự linh hoạt khi bạn cần tạo các component cần thông tin phức tạp hơn các chuỗi và số đơn giản.
Vì cấu trúc này dữ liệu khá phức tạp, nên mình sẽ thay đổi lại cấu trúc HTML từ dạng bảng thành flex-box và css cho nó.
Sửa file components/product/ProductCompWrapper.js
import React from "react";
import PropTypes from 'prop-types'
const ProductComWrapper = ({ children, details, title }) => {
return (
<div className="box-item" title={title}>
{children}
<span> {title}: {details}</span>
</div>
)
}
export default ProductComWrapper
ProductComWrapper.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element.isRequired
]),
title: PropTypes.string.isRequired,
details: PropTypes.element,
}
ProductComWrapper.defaultProps = {
details: null,
}
Prop details này sẽ cùng kiểu với child, nên là là optional. Để làm cho nó optional, bạn thêm một giá trị mặc định là null. Trong trường hợp này, nếu người dùng không chuyển thông tin chi tiết, thành phần sẽ vẫn có hiệu lực và sẽ không hiển thị thêm bất kỳ thứ gì.
Sửa lại cấu trúc cũ file App.js (không cần bọc ProductComWarpper bên ngooài):
import './App.css';
import data from './data/data';
import ProductComp from './components/product/ProductComponent';
import ProductComWrapper from './components/product/ProductCompWrapper';
const App = () => {
// return (<></>)
return (
<div className='container'>
<div className='box-wrapper'>
<div className='box-top'>
<div className='box-item'>
<strong>ID</strong>
<strong>Product</strong>
<strong>Type</strong>
<strong>PPU</strong>
<strong>Batters</strong>
<strong>Topping</strong>
<strong>Detail</strong>
</div>
</div>
<div className='box-bottom'>
{data.map((item) => {
return (<ProductComp id={item.id} name={item.name} type={item.type} ppu={item.ppu} batters={item.batters} topping={item.topping} />)
})}
</div>
</div>
</div>
)
}
export default App;
Sửa file App.css
.box-item{
display: flex;
width: 1200px;
margin: auto;
}
.box-item > strong,.box-item > span{
border: 1px solid #e1e1e1;
display: block;
width: 14.2857%;
padding: 5px;
box-sizing: border-box;
}
.box-item ul {
list-style: none;
padding: 0px;
}
Sửa file components/product/ProductComponent.js
import React from "react";
import PropTypes from 'prop-types'
import BatterComp from "./BatterComponent";
import ToppingComp from "./ToppingComponent";
import ProductComWrapper from "./ProductCompWrapper";
const ProductComp = ({id,name,type,ppu,...props}) => {
return (<ProductComWrapper className="data-box" title="Description" details={<>This is props passing by of ProductWarpper</>}>
<span>{id}</span>
<span>{name}</span>
<span>{type}</span>
<span>{ppu}</span>
<span><BatterComp {...props} /></span>
<span><ToppingComp {...props} /></span>
</ProductComWrapper>)
}
export default ProductComp
ProductComp.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
ppu: PropTypes.number.isRequired,
// batters: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
batters: PropTypes.shape(
{
batter : PropTypes.arrayOf(PropTypes.object)
}
),
topping: PropTypes.arrayOf(PropTypes.object),
showBatters: PropTypes.func,
showTopping: PropTypes.func
}
ProductComp.defaultProps = {
ppu: 0
}
Output:
Ở ví dụ phía trên, chúng ta thấy component ProductComp mặc dù là Component con đưược gọi thông qua {children} , nhưng nó lại có khả năng truyền props cho ProductCompWrapper là component cha sử dụng. Chúng ta hoàn toàn có thể tùy biến 1 trong 2 cách trên và dưới để mã nguồn linh hoạt, dễ bảo trì hơn.
Bài tập
Bài tập: Xây dựng Ứng dụng Quản lý Xe với ReactJS
Phần 1: Setup và Khởi tạo Dự án
Setup Project ReactJS
- Tạo một dự án ReactJS mới bằng cách sử dụng Create React App.
- Cấu hình dự án để sử dụng hooks nhằm quản lý state hiệu quả.
Tạo Component Khởi đầu
- Tạo một component App.js.
- Trong App.js, khởi tạo state bao gồm:
- Một biến
vehicles
kiểu mảng chứa thông tin các xe với các thuộc tính:const [vehicles, setVehicles] = useState([ { id: 1, name: 'Honda Civic', image: '/path/to/civic', status: 'available', price: 20 }, { id: 2, name: 'Toyota Corolla', image: '/path/to/corolla', status: 'available', price: 25 }, { id: 3, name: 'Tesla Model 3', image: '/path/to/tesla', status: 'available', price: 50 }, ]);
- Một biến
rentedVehicles
kiểu mảng để lưu danh sách xe đã thuê. - Một biến
budget
kiểu số để theo dõi ngân sách còn lại (khởi tạo là 500).
- Một biến
Phần 2: Hiển thị Danh sách Xe
Tạo Component Vehicle
- Tạo một component Vehicle.js.
- Component này nhận thông tin xe thông qua props và hiển thị:
- Tên xe.
- Hình ảnh.
- Trạng thái (available/đã thuê).
- Nút "Thuê xe".
Hiển thị Danh sách Xe
- Tạo một component VehicleList.js.
- Sử dụng hàm
map
để lặp qua danh sách trong biếnvehicles
và hiển thị mỗi xe bằng component Vehicle.
Hiển thị trên Trang chính
- Trong App.js, sử dụng component VehicleList để hiển thị danh sách xe.
Phần 3: Thuê Xe
Tạo Component RentVehicle
- Thêm xử lý sự kiện khi người dùng nhấn nút "Thuê xe":
- Hiển thị alert "Xử lý thuê xe".
- Viết thuật toán kiểm tra ngân sách:
- Nếu ngân sách đủ, trừ đi giá thuê xe và cập nhật trạng thái xe thành "đã thuê".
- Nếu không đủ ngân sách, hiển thị alert "Ngân sách không đủ để thuê xe này".
- Nếu thuê thành công, hiển thị alert "Thuê thành công + Tên xe" và cập nhật xe vào danh sách
rentedVehicles
.
Cập nhật Danh sách Xe đã Thuê
- Sau khi thuê xe, cập nhật danh sách xe đã thuê hiển thị trên trang chính.
Phần 4: Xây dựng Garage
Tạo Component Garage
- Tạo một component Garage.js.
- Component này hiển thị danh sách các xe đã thuê.
Router và Chuyển đổi giữa các Trang
- Thiết lập router trong App.js:
- Một route cho trang chính hiển thị tất cả xe (VehicleList).
- Một route cho bộ sưu tập xe đã thuê (Garage).