Trong bài viết này, chúng ta sẽ khám phá Higher-Order Components trong ReactJS, một tính năng rất hữu ích trong việc tái sử dụng tài nguyên.
Trong dự án ReactJS, việc tái sử dụng các thành phần và phân chia chúng theo một cách hợp lý là rất quan trọng. Khi các thành phần được tổ chức thành các phần riêng biệt, việc tái sử dụng sẽ giúp cho trình duy trì dự án trở nên dễ dàng hơn. Higher-Order Components là một phương pháp cho phép chúng ta thực hiện điều này một cách đơn giản và hiệu quả.
1. Higher-Order Components là gì?
Thành phần bậc cao (HOC) được định nghĩa là một hàm nhận một thành phần làm đối số và trả về một thành phần mới. HOC không phải là một tính năng cụ thể trong React hay bất kỳ trình lập trình ngôn ngữ nào, mà là một thành phần kỹ thuật phát triển.
Để hiểu rõ hơn về lý do sử dụng HOC, hãy xem xét một ví dụ đơn giản:
Giả sử chúng ta xây dựng một thành phần hiển thị một hình ảnh, và mỗi khi di chuyển con trỏ chuột vào hình ảnh, hình ảnh sẽ trở nên mờ đi.
1
2
3
4
5
6
7
số 8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import React from "react" ; const Image = (props) => { }; export default class HoverComponent extends React.Component { constructor(props) { super (props); this .state = { opacity: 1, }; //bind this this .onMouseLeave = this .onMouseLeave.bind( this ); this .onMouseEnter = this .onMouseEnter.bind( this ); } //Được gọi khi chuột được di vào onMouseEnter() { this .setState({ opacity: 0.5, }); } //Được gọi khi chuột được rời đi onMouseLeave() { this .setState({ opacity: 1, }); } render() { return ( <div style={{ opacity: this .state.opacity }} onMouseEnter={ this .onMouseEnter} onMouseLeave={ this .onMouseLeave} > <Image /> </div> ); } } |
Trong chương trình trên, khi muốn hiển thị nhiều hình ảnh thì phải xây dựng rất nhiều mục HoverComponent
đích chỉ nhằm làm mờ hình ảnh. Điều này không cần thiết. Vậy hãy làm sao để làm mờ nhiều ảnh mà không cần phải viết lại HoverComponent
cho mỗi ảnh. Chúng ta sẽ cùng tìm hiểu cách giải quyết cho bài toán này ở phần ví dụ nhé.
2. Triển khai ví dụ
Ta sẽ giải quyết bài toán ở đầu bài, lúc này sẽ cần sử dụng Higher Order Components (HOC) có chức năng làm mờ ảnh. Cùng bắt đầu xây dựng một ngôi nhà chung mang tên HOC withHoverOpacity
:
1
2
3
4
5
6
7
số 8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
//Đây được gọi là một HOC, nó nhận vào 1 component //và trả ra một component const withHoverOpacity = (ImageComponent) => { return class extends React.Component { constructor(props) { super(props); this.state = { opacity: 1, } ; //bind this this . onMouseLeave = this . onMouseLeave . bind(this); this . onMouseEnter = this . onMouseEnter . bind(this); } //Được gọi khi chuột được di vào onMouseEnter() { this.setState({ opacity: 0.5, } ); } //Được gọi khi chuột được rời đi onMouseLeave() { this.setState({ opacity: 1, } ); } render() { return ( <div style={{ opacity: this.state.opacity } } onMouseEnter= {this.onMouseEnter} onMouseLeave= {this.onMouseLeave} > <ImageComponent /> </ div > ); } }; }; |
Một HOC nhận vào một thành phần và trả về một thành phần, bên trong mình đã xây dựng thành công một HOC. Tiếp theo, để sử dụng nó bạn chỉ cần gọi nó.
1
2
3
4
5
6
7
số 8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//Các component là các ảnh cần Hover const Image1 = (props) => { }; const Image2 = (props) => { return ( <img alt= "facebook" /> ); }; //Lúc này mình truyền vào HOC một component //và mình sẽ nhận vào một component mới //Ở đây mình có thể hiển thị rất nhiều ảnh // mà không cần phải xây dựng component hỗ trợ việc làm //mờ ảnh quá nhiều const ImageWithHoverOpacity1 = withHoverOpacity(Image1); const ImageWithHoverOpacity2 = withHoverOpacity(Image2); |
Lúc này thành phần này ImageWithHoverOpacity1
là ImageWithHoverOpacity2
2 thành phần đã được thêm hiệu ứng làm mờ ảnh bằng HOC. Bây giờ, ta chỉ cần render nó ra:
1
2
3
4
5
6
7
số 8
9
|
//Hiển thi component export default function () { return ( <> <ImageWithHoverOpacity1 /> <ImageWithHoverOpacity2 /> </> ); } |
Lúc này ta sẽ nhận được kết quả gần giống với đầu bài, nhưng các đoạn mã sẽ được tái sử dụng nhiều lần. Khởi chạy ứng dụng và đây là kết quả
Truyền props và các tùy chọn khác cho HOC
HOCs thường cần truyền các props từ component cha xuống WrappedComponent
. Điều này được thực hiện bằng cách sử dụng toán tử spread (...
).
javascript
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
componentDidUpdate() {console.log(`Component ${WrappedComponent.name} updated`);
}
render() {
return <WrappedComponent {…this.props} />;
}
};
}
Việc truyền các props này giúp đảm bảo rằng WrappedComponent
có thể nhận được tất cả các props cần thiết từ component cha.
Tái sử dụng logic bằng HOC
Một trong những lợi ích lớn nhất của HOC là khả năng tái sử dụng logic giữa các components khác nhau. Giả sử chúng ta có nhiều components cần tính năng logging, thay vì thêm logic logging vào từng component, chúng ta có thể tạo một HOC và sử dụng nó với tất cả các components cần tính năng này.
javascript
const EnhancedComponent1 = withLogging(Component1);
const EnhancedComponent2 = withLogging(Component2);
const EnhancedComponent3 = withLogging(Component3);
HOCs và lifecycle methods
HOCs có thể can thiệp vào lifecycle methods của WrappedComponent
để thêm hoặc thay đổi chức năng. Điều này cho phép HOCs thực hiện các tác vụ như kiểm soát rendering, thêm tính năng caching, hoặc quản lý các sự kiện.
javascript
function withAuth(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
if (!this.isAuthenticated()) {
// Redirect to login page
window.location.href = '/login';
}
}
isAuthenticated() {// Logic kiểm tra authentication
return true; // Giả sử người dùng đã authenticated
}
render() {
return <WrappedComponent {…this.props} />;
}
};
}
const ProtectedComponent = withAuth(SomeComponent);
Các HOCs nâng cao
HOCs không chỉ giới hạn ở việc thêm các props hoặc quản lý lifecycle methods. Chúng có thể được sử dụng để thêm các tính năng phức tạp hơn như caching, memoization, hoặc thậm chí là quản lý state.
Ví dụ: HOC với caching
javascript
function withCaching(WrappedComponent) {
const cache = {};
return class extends React.Component {render() {
const { id } = this.props;
if (!cache[id]) {
cache[id] = <WrappedComponent {…this.props} />;
}
return cache[id];
}
};
}
const CachedComponent = withCaching(SomeComponent);
Trong ví dụ này, withCaching
HOC tạo ra một đối tượng cache để lưu trữ các thể hiện của WrappedComponent
. Nếu WrappedComponent
đã được render cùng một lúc id
, HOC sẽ sử dụng bộ nhớ đệm phiên bản thay vì tạo một phiên bản mới.
Các nguyên tắc khi sử dụng HOC
Mặc dù HOCs là một công cụ mạnh mẽ, có một số nguyên tắc cần phải tuân theo để sử dụng chúng theo một cách hiệu quả:
- Không thể thay đổi thành phần ban đầu : HOCs nên được sử dụng để bổ sung chức năng, không nên thay đổi hoặc làm phức tạp
WrappedComponent
. - Truyền props một cách rõ ràng : HOCs nên truyền tất cả các props xuống
WrappedComponent
để đảm bảo tính trong suốt. - Đặt tên HOCs theo cách rõ ràng : Sử dụng các tên mô tả cho HOCs để dễ dàng hiểu được chức năng của chúng. Ví dụ:
withLogging
,withAuth
,withCaching
. - Sử dụng các HOC theo cách có hệ thống : Khi sử dụng nhiều HOC, hãy tổ chức chúng theo cách có hệ thống để tránh sự phức tạp và khó hiểu.
HOCs so với Hooks
Mặc dù HOCs là một công cụ mạnh mẽ, với sự ra đời của Hooks trong React 16.8, nhiều trường hợp sử dụng HOCs có thể được thay thế bằng Hooks. Hooks cung cấp một cách tiếp cận linh hoạt hơn để xử lý trạng thái và các tác dụng phụ trong các thành phần hàm mà không cần phải sử dụng HOC.
Ví dụ, thay vì sử dụng HOC để thêm logic logging, chúng ta có thể sử dụng hook useEffect
trong function component.
javaScript
import React, { useEffect } from 'react';
hàm UserComponent ( { name } ) {
useEffect ( () => {
console . log ( `Component UserComponent đã được gắn kết` );
return () => {
console . log ( `Component UserComponent chưa được gắn kết` );
};
}, []);
trả về <div> Người
dùng : {tên } </div> ; }
Higher-Order Components là một kỹ thuật quan trọng và mạnh mẽ trong React để tái sử dụng logic giữa các thành phần. Bằng cách bao bọc một thành phần trong một thành phần mới và thêm vào hoặc thay đổi chức năng của nó, HOCs cho phép bạn xây dựng các ứng dụng React phức tạp và linh hoạt hơn. Tuy nhiên, với sự ra đời của Hooks, việc sử dụng HOCs có thể giảm bớt trong nhiều trường hợp, mặc dù chúng vẫn là một công cụ quý giá trong bộ công cụ của bất kỳ nhà phát triển React nào. Như vậy là mình đã giới thiệu hoàn thành Higher-Order Components (HOC) trong ReactJS. Đây là kiến thức rất cơ bản về nó nhưng cũng hết sức quan trọng trong quá trình làm việc với ReactJS sau này. Mong rằng bài viết sẽ có ích cho bạn.