Giới thiệu props trong ReactJS và tùy chỉnh thành phần (component) với props
- 23-09-2022
- Toanngo92
- 0 Comments
Để bắt đầu bài viết này, chúng ta có thể tạo dự án mới hoặc xóa trắng các demo cũ và áp dụng code mẫu sau trong file App.js
Tạo một 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
Tạo file components/product/ProductComponent.js
const ProductComp = () => {
return (<div>Product Component</div>)
}
export default ProductComp
Sửa code file App.js như sau:
import './App.css';
import data from './data/data';
import ProductComp from './components/product/ProductComponent';
const App = () => {
return (
<div className='container'>
{data.map((item) => {
return (<ProductComp />)
})}
</div>
)
}
export default App;
Cấu trúc thư mục và output:
Ở đây, chúng ta thấy dữ liệu của ProductComponent vẫn đang tĩnh, mình sẽ bắt đầu truyền props để có thể sử dụng và in ra thuộc tính name bên trong đối tượng sản phẩm của mảng data như ví dụ dưới.
Mục lục
Thêm props vào component
Sửa file App.js thành cấu trúc như sau:
import './App.css';
import data from './data/data';
import ProductComp from './components/product/ProductComponent';
const App = () => {
return (
<div className='container'>
{data.map((item) => {
return (<ProductComp key={item.id} name={item.name} />)
})}
</div>
)
}
export default App;
Lưu ý: chúng ta cần đưa key vào ProductComp khi truyền props, mặc dù dự án vẫn chạy nhưng nếu không có key, compiler sẽ warning:
react-jsx-dev-runtime.development.js:87
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `App`. See https://reactjs.org/link/warning-keys for more information.
at ProductComp (http://localhost:3000/static/js/bundle.js:108:20)
at App
Sửa nội dung file ProductComponent.js
const ProductComp = (props) => {
const name = props
console.log(name)
return (<div>{name.name}</div>)
}
export default ProductComp
Output
Chúng ta thấy rằng giá trị của biến name mình vừa gán bằng props truyền vào khi log ra sẽ là một object, chúng ta truy vấn trực tiếp vào thuộc tính name của biến để có thể in dữ liệu ra JSX.
Nâng cao hơn, chúng ta sẽ chỉnh sửa mã nguồn để có thể in ra bảng dữ liệu tất cả sản phẩm của mảng data.
Cách 1: truyền toàn bộ dữ liệu qua 1 biến
Sửa 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} product={item} />)
})}
</tbody>
</table>
</div>
)
}
export default App;
Sửa nội dung file ProductComponent.js
const ProductComp = (props) => {
const product = props.product
console.log(product)
return (<tr>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.type}</td>
<td>{product.ppu}</td>
</tr>)
}
export default ProductComp
Output
Cách 2: truyền dữ liệu thành các biến khác nhau
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} />)
})}
</tbody>
</table>
</div>
)
}
export default App;
Sửa nội dung file ProductComponent.js
const ProductComp = ({id,name,type,ppu}) => {
return (<tr>
<td>{id}</td>
<td>{name}</td>
<td>{type}</td>
<td>{ppu}</td>
</tr>)
}
export default ProductComp
Output vẫn không thay đổi. (Lưu ý, để hứng giá trị trong ProductComp, tham số hứng sẽ là một object, và mỗi thuộc tính từ <ProductComp /> trong App.js truyền vào sẽ tương ứng lần lượt với thuộc tính của object đó.)
Tiếp theo, chúng ta kết hợp sự kiện onClick để hiển thị các thông tin còn lại của dữ liệu mẫu ra như batters, topping ( 2 thuộc tính này giá trị là object mảng và mảng ).
Sửa 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;
Sửa nội dung file ProductComponent.js
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
Output:
Tạo Predictable Props với PropTypes và defaultProps
Sau khi thêm props vào component của mình, bạn sẽ sử dụng PropTypes để xác định loại dữ liệu mà bạn mong đợi một thành phần nhận được. PropTypes là một hệ thống kiểu đơn giản để kiểm tra xem dữ liệu có khớp với kiểu mong đợi trong thời gian chạy hay không.
Chúng đóng vai trò như cả tài liệu hướng dẫn và công cụ kiểm tra lỗi sẽ giúp ứng dụng của bạn có thể dự đoán được khi nó mở rộng quy mô.
Bước 1: nếu phiên bản ReactJS của bạn phiên bản từ 15.5 trở lên chạy lệnh cài package Proptypes bằng lệnh:
npm install --save prop-types
Chúng ta sửa cấu trúc file components/product/ProductComponent.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
}
Như bạn có thể thấy, có rất nhiều PropTypes khác nhau. Đây chỉ là một ví dụ nhỏ, xem tài liệu React chính thức để xem những tài liệu khác mà bạn có thể sử dụng:
Typechecking With PropTypes – React (reactjs.org)
Hãy bắt đầu với prop id. Ở đây, bạn đang chỉ định rằng id phải là một chuỗi. Các Thuộc tính name, type cũng vậy. ppu là một số, có thể bao gồm cả số thực (float) và số nguyên (integer). showBatters,showTopping là một hàm (func). Mặt khác, batters có một chút khác biệt. Trong trường hợp này, bạn đang chỉ định topping sẽ là một array, nhưng bạn cũng cần chỉ định array này sẽ chứa những gì. Trong trường hợp này, array này sẽ chứa một object. Nếu bạn muốn kết hợp các kiểu dữ liệu, bạn có thể sử dụng một hỗ trợ khác có tên là oneOfType, lấy một mảng các PropType hợp lệ. Bạn có thể sử dụng oneOfType ở bất cứ đâu, vì vậy nếu bạn muốn ppu là một số hoặc một chuỗi, bạn có thể thay đổi nó thành sau:
ppu: PropTypes.oneOfType ([PropTypes.number, PropTypes.string])
Với prop batters cũng phức tạp hơn một chút. Trong trường hợp này, bạn đang chỉ định một đối tượng, nhưng nói rõ hơn một chút, bạn đang nói rõ những gì bạn muốn đối tượng chứa. Để làm điều đó, bạn sử dụng PropTypes.shape, lấy một đối tượng với các trường bổ sung sẽ cần PropTypes của riêng chúng. Hãy để ý trước định nghĩa sử dụng shape mình có comment một lệnh phía trên, với cách sử dụng cũ đó, props sẽ không rõ ràng và khó biểu diễn dữ liệu phức tạp một cách chính xác.
Hiện tại, tất cả dữ liệu đều được tạo hình tốt và khớp với các props. Để xem điều gì sẽ xảy ra nếu PropTypes không khớp, hãy mở dữ liệu của bạn ở trường ppu:
ppu: PropTypes.string.isRequired,
Ứng dụng sẽ không lỗi, tuy nhiên console.log sẽ trả ra warning để thông báo cho chúng ta biết dữ liệu đang sử dụng chưa đúng:
react-jsx-dev-runtime.development.js:87
Warning: Failed prop type: Invalid prop `ppu` of type `number` supplied to `ProductComp`, expected `string`.
at ProductComp (http://localhost:3000/static/js/bundle.js:165:5)
at App
Trong tình huống phía trên, chúng ta thấy có thuộc tính isRequired, nghĩa là chúng được yêu cầu bắt buộc. Nếu bạn không đưa dữ liệu bắt buộc vào, mã sẽ vẫn được biên dịch, nhưng bạn sẽ thấy lỗi thời gian chạy trong console.log.
Nếu props không được yêu cầu bắt buộc, bạn có thể thêm giá trị mặc định. Một phương pháp hay là luôn thêm giá trị mặc định để tránh lỗi thời gian chạy nếu không cần phải có giá trị hỗ trợ. Ví dụ: trong thành phần AnimalCard, bạn đang gọi một hàm với dữ liệu bổ sung. Nếu nó không có ở đó, chức năng sẽ thử và sửa đổi một đối tượng không tồn tại và ứng dụng sẽ gặp sự cố.
Để ngăn chặn sự cố này, hãy thêm một defaultProp nếu không có dữ liệu cho các thuộc tính, ví dụ:
ProductComp.defaultProps = {
ppu: 0
}
Giờ đây, các props của bạn đã được ghi chép đầy đủ và được yêu cầu hoặc có mặc định để đảm bảo mã có thể dự đoán được. Điều này sẽ giúp các nhà phát triển tương lai (bao gồm cả chính bạn) hiểu một component cần prop gì. Nó sẽ giúp bạn dễ dàng trao đổi và sử dụng lại các component của mình hơn bằng cách cung cấp cho bạn thông tin đầy đủ về cách component sẽ sử dụng dữ liệu mà nó đang nhận.