Mình muốn chia sẻ với các bạn về một khái niệm khá thú vị trong React là Render Props Pattern. Nếu bạn đang tìm cách để tái sử dụng logic giữa các components hoặc muốn tách biệt phần logic và giao diện trong ứng dụng React của mình, thì bài viết này dành cho bạn.
1. Render Props pattern là gì?
Render Props là một kỹ thuật trong React để share code giữa các component bằng cách sử dụng một prop có giá trị là một hàm. Thuật ngữ "render prop" đề cập đến một prop trên một component mà giá trị của nó là một hàm cung cấp để điều khiển những gì sẽ được render bởi component đó.
Nói một cách đơn giản, thay vì hard-code nội dung render bên trong component, bạn có thể cho phép component đó nhận một hàm render thông qua props và sử dụng nó để render nội dung tùy chỉnh.
Ví dụ: Giả sử bạn muốn tạo một component theo dõi vị trí con trỏ chuột và hiển thị nó trên màn hình. Bạn có thể tạo một component tên là MouseTracker
sử dụng Render Props để chia sẻ logic theo dõi vị trí chuột với các component khác.
import React from 'react';
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = event => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
export default MouseTracker;
Sử dụng MouseTracker
:
import React from 'react';
import MouseTracker from './MouseTracker';
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<h1>
Vị trí chuột: ({x}, {y})
</h1>
)}
/>
);
}
export default App;
MouseTracker
là component theo dõi vị trí chuột và lưu trữ trong state.- Thay vì tự render nội dung, nó sử dụng
this.props.render(this.state)
để gọi đến hàm render được truyền qua props. - Trong component App, mình sử dụng
MouseTracker
và truyền vào một hàm render thông qua prop render. Hàm này nhận đối số là state củaMouseTracker
và hiển thị vị trí chuột.
2. Tại sao nên sử dụng Render Props?
- Tái sử dụng logic giữa các components mà không cần phải sử dụng Higher-Order Components (HOC) hoặc mixins.
- Cho phép bạn định nghĩa cách render nội dung từ bên ngoài component, giúp tăng tính linh hoạt và tái sử dụng.
- Tách biệt rõ ràng giữa phần logic và phần hiển thị, giúp code dễ đọc và bảo trì hơn.
Cùng xem một ví dụ cụ thể để hiểu rõ hơn về Render Props. Giả sử bạn muốn tạo một component cho phép người dùng nhập nhiệt độ theo độ Celsius và hiển thị giá trị chuyển đổi sang Kelvin và Fahrenheit.
Đầu tiên, tạo một component Input có state để lưu giá trị người dùng nhập vào.
import React, { useState } from "react";
function Input(props) {
const [value, setValue] = useState("");
return (
<input
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Nhập nhiệt độ (°C)"
/>
);
}
export default Input;
Chúng ta muốn Input có thể hiển thị nhiệt độ sau khi chuyển đổi sang Kelvin và Fahrenheit. Thay vì hard-code việc hiển thị này bên trong Input, chúng ta sẽ sử dụng render props.
function Input(props) {
const [value, setValue] = useState("");
const celsius = parseFloat(value);
return (
<>
<input
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Nhập nhiệt độ (°C)"
/>
{props.renderKelvin && !isNaN(celsius) && props.renderKelvin(celsius)}
{props.renderFahrenheit && !isNaN(celsius) && props.renderFahrenheit(celsius)}
</>
);
}
Ở đoạn code này, mình sẽ check xem renderKelvin
và renderFahrenheit
có được truyền vào hay không và giá trị nhập vào có phải là số hợp lệ không. Sau đó, mình gọi các hàm render này và truyền vào giá trị nhiệt độ Celsius.
Bây giờ, mình sẽ sử dụng component Input trong component App và truyền vào các hàm render tương ứng.
import React from "react";
import Input from "./Input";
//----------------------------------------------------------------
function App() {
return (
<div>
<h1>Chuyển Đổi Nhiệt Độ</h1>
<Input
renderKelvin={(celsius) => (
<div>{(celsius + 273.15).toFixed(2)} K</div>
)}
renderFahrenheit={(celsius) => (
<div>{((celsius * 9) / 5 + 32).toFixed(2)} °F</div>
)}
/>
</div>
);
}
export default App;
Ở App, mình truyền vào hai hàm renderKelvin
và renderFahrenheit
, mỗi hàm nhận vào giá trị Celsius và trả về một phần tử JSX hiển thị kết quả chuyển đổi.
3. Ưu, nhược điểm
3.1 Ưu điểm
- Render props cho phép component cha kiểm soát hoàn toàn cách thức render của component con, giúp tùy biến giao diện một cách linh hoạt.
- Thay vì sử dụng Context API để truyền dữ liệu qua nhiều cấp components, render props có thể truyền dữ liệu trực tiếp thông qua hàm render.
- HOC có thể gây ra một số vấn đề như "wrapper hell" và khó theo dõi luồng dữ liệu. Render props giúp tránh những vấn đề này bằng cách cung cấp một cách rõ ràng hơn để truyền dữ liệu và logic.
3.2 Nhược điểm
- Việc phải truyền nhiều hàm render có thể làm code trở nên dài dòng và phức tạp.
- Khi sử dụng nhiều render props lồng nhau, việc theo dõi luồng dữ liệu và logic có thể trở nên khó khăn.
- Không Tương Thích với PureComponent và shouldComponentUpdate: việc sử dụng render props có thể gây ra vấn đề với các lifecycle methods như shouldComponentUpdate, do các hàm render được tạo mới mỗi lần render.
4. Hướng dẫn sử dụng Render Props trong React
Để bạn có thể dễ dàng áp dụng được, mình sẽ đi theo từng bước như thế này:
B1: Xác định logic cần share
Trước tiên, xác định phần logic mà bạn muốn chia sẻ giữa các component. Đây thường là các tác vụ như: theo dõi sự kiện, lấy dữ liệu từ API, hoặc quản lý trạng thái chung. Ví dụ như bạn muốn tạo một component quản lý trạng thái mở rộng (expanded) cho các mục trong danh sách.
B2: Tạo component sử dụng Render Props
Tạo một component nhận vào một prop là hàm render. Component này sẽ chứa logic cần share và gọi hàm render đó để quyết định nội dung hiển thị.
import React from 'react';
class Expandable extends React.Component {
state = { expanded: false };
toggleExpand = () => {
this.setState(prevState => ({ expanded: !prevState.expanded }));
};
render() {
return this.props.children({
expanded: this.state.expanded,
toggleExpand: this.toggleExpand,
});
}
}
export default Expandable;
Bước 3: Sử dụng component với Render Props
Khi sử dụng component này, bạn truyền vào một hàm render dưới dạng children hoặc một prop cụ thể.
import React from 'react';
import Expandable from './Expandable';
function App() {
return (
<Expandable>
{({ expanded, toggleExpand }) => (
<div>
<button onClick={toggleExpand}>
{expanded ? 'Thu gọn' : 'Mở rộng'}
</button>
{expanded && <div>Nội dung mở rộng</div>}
</div>
)}
</Expandable>
);
}
export default App;
Expandable
quản lý trạng tháiexpanded
và cung cấp hàmtoggleExpand
.- Trong App, bạn sử dụng
Expandable
và truyền vào một hàm render thông quachildren
. Hàm này nhận các tham số từExpandable
và quyết định cách hiển thị giao diện.
Bước 4: Tùy chỉnh và mở rộng
Bạn có thể sử dụng cùng component Expandable
ở nhiều nơi khác nhau trong dự án với cách hiển thị khác nhau.
function FAQItem({ question, answer }) {
return (
<Expandable>
{({ expanded, toggleExpand }) => (
<div>
<h3 onClick={toggleExpand}>{question}</h3>
{expanded && <p>{answer}</p>}
</div>
)}
</Expandable>
);
}
5. Khi nào nên sử dụng Render Props?
Render Props là một kỹ thuật tốt, nhưng không phải lúc nào cũng là giải pháp tốt nhất. Dưới đây là một số trường hợp nên và không nên sử dụng Render Props, bạn nên xem xét, cân nhắc trước khi sử dụng.
5.1 Nên sử dụng Render Props
- Khi bạn có logic trong code cần được sử dụng lại ở nhiều nơi nhưng không muốn lặp đi lặp lại đoạn code đó. Ví dụ: Theo dõi vị trí chuột, trạng thái hover, hoặc kết nối WebSocket.
- Khi bạn muốn component cha quyết định cách thức render của component con. Ví dụ: component
DataFetcher
lấy dữ liệu và cho phép bạn quyết định cách hiển thị dữ liệu đó. - Khi sử dụng nhiều HOC có thể dẫn đến "wrapper hell", Render Props là một giải pháp tốt để thay thế.
5.2 Không nên sử dụng Render Props
- Nếu bạn sử dụng React phiên bản 16.8 trở lên, Hooks như
useState
,useEffect
có thể thay thế nhu cầu sử dụng Render Props trong nhiều trường hợp. - Việc truyền hàm inline trong Render Props có thể gây ra re-render không cần thiết. Trong trường hợp này, HOC hoặc Hooks có thể sẽ hiệu quả hơn với bạn.
- Khi bạn sử dụng nhiều Render Props lồng nhau, code sẽ hơi khó đọc và khó bảo trì.
5. Kết luận
Render Props trong React giúp bạn chia sẻ và tái sử dụng logic giữa các components một cách linh hoạt. Mặc dù Hooks đã thay đổi cách bạn quản lý state và logic trong React, Render Props vẫn có chỗ đứng và hữu ích trong nhiều trường hợp.
Hy vọng qua bài viết này, bạn đã hiểu rõ hơn về Render Props Pattern và cách áp dụng nó trong dự án của mình.
Các bài viết liên quan:
Bài viết liên quan
HOCs Pattern là gì? Hướng dẫn triển khai Hocs Pattern trong dự án React
Dec 02, 2024 • 7 min read
Hooks Pattern là gì? Hướng dẫn áp dụng Hooks Pattern trong dự án React
Nov 28, 2024 • 11 min read
Promise là gì? Hướng dẫn sử dụng Promise trong dự án React
Nov 27, 2024 • 7 min read
Async/await là gì? Hướng dẫn sử dụng Async/await trong dự án React
Nov 26, 2024 • 8 min read
Một số Phương pháp cải thiện Performance trong dự án React
Nov 25, 2024 • 16 min read
Hướng dẫn tích hợp Redux và React Query trong dự án React Vite
Nov 22, 2024 • 8 min read