ReactJS - Higher-Order Components: A Beginner's Guide

Xin chào các bạnfuture React wizards! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới của Higher-Order Components (HOCs) trong ReactJS. Đừng lo lắng nếu bạn mới bắt đầu học lập trình - tôi sẽ là người hướng dẫn thân thiện của bạn, và chúng ta sẽ cùng nhau từng bước. Cuối cùng của bài hướng dẫn này, bạn sẽ có khả năng tạo ra HOCs như một chuyên gia!

ReactJS - Higher Order Components

What are Higher-Order Components?

Trước khi chúng ta bắt đầu, hãy hiểu Higher-Order Components là gì. Hãy tưởng tượng bạn đang ở một cửa hàng burger. Bạn gọi một burger thường, nhưng sau đó bạn quyết định thêm phô mai, rau và bacon. Quá trình thêm những món ăn này vào burger của bạn tương tự như những gì HOCs làm trong React!

Trong thuật ngữ React, một Higher-Order Component là một hàm nhận một component và trả về một component mới với một số chức năng được thêm vào. Nó giống như một loại nước sốt đặc biệt giúp nâng cao các component hiện có mà không thay đổi thành phần cốt lõi của chúng.

Dưới đây là một ví dụ đơn giản:

// Điều này giống như burger thường của chúng ta
const PlainBurger = () => <div>?</div>;

// Điều này là HOC của chúng ta, giống như thêm topping
const addCheese = (Burger) => {
return () => (
<div>
<Burger />
<span>?</span>
</div>
);
};

// Đây là burger của chúng ta với phô mai!
const CheeseBurger = addCheese(PlainBurger);

Trong ví dụ này, addCheese là Higher-Order Component của chúng ta. Nó nhận PlainBurger và trả về một component mới với phô mai được thêm vào!

How to Use Higher-Order Components

Bây giờ chúng ta đã hiểu khái niệm, hãy xem cách chúng ta có thể sử dụng HOCs trong một tình huống thực tế hơn. Giả sử chúng ta có nhiều component cần lấy dữ liệu từ một API. Thay vì viết cùng một logic lấy dữ liệu trong mỗi component, chúng ta có thể tạo một HOC để xử lý việc này cho chúng ta.

Dưới đây là cách chúng ta có thể làm điều đó:

import React, { useState, useEffect } from 'react';

// Đây là HOC của chúng ta
function withDataFetching(WrappedComponent, dataSource) {
return function(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
fetch(dataSource)
.then(response => response.json())
.then(result => {
setData(result);
setLoading(false);
})
.catch(e => {
setError(e);
setLoading(false);
});
}, []);

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return <WrappedComponent data={data} {...props} />;
}
}

// Đây là một component đơn giản sẽ nhận dữ liệu đã lấy
function UserList({ data }) {
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}

// Chúng ta sử dụng HOC để tạo một component mới với khả năng lấy dữ liệu
const UserListWithData = withDataFetching(UserList, 'https://api.example.com/users');

// Bây giờ chúng ta có thể sử dụng UserListWithData trong ứng dụng của chúng ta!
function App() {
return (
<div>
<h1>User List</h1>
<UserListWithData />
</div>
);
}

Hãy phân tích điều này:

  1. Chúng ta xác định HOC withDataFetching. Nó nhận hai tham số: component mà chúng ta muốn bao bọc (WrappedComponent) và URL để lấy dữ liệu (dataSource).

  2. Trong withDataFetching, chúng ta tạo và trả về một component mới. Component này sử dụng React hooks để quản lý trạng thái và hiệu ứng phụ thuộc.

  3. Chúng ta sử dụng useState để quản lý trạng thái data, loading, và error.

  4. Chúng ta sử dụng useEffect để lấy dữ liệu khi component được装载. Khi dữ liệu được lấy, chúng ta cập nhật trạng thái tương ứng.

  5. Tùy thuộc vào trạng thái, chúng ta hoặc hiển thị thông báo đang tải, thông báo lỗi, hoặc hiển thị WrappedComponent với dữ liệu đã lấy.

  6. Chúng ta tạo một component đơn giản UserList mà mong đợi nhận dữ liệu dưới dạng prop và hiển thị nó.

  7. Chúng ta sử dụng HOC để tạo một component mới UserListWithData có khả năng lấy dữ liệu.

  8. Cuối cùng, chúng ta sử dụng UserListWithData trong component App.

Applying HOC Components

Bây giờ chúng ta đã thấy cách tạo và sử dụng một HOC, hãy xem một số trường hợp sử dụng phổ biến và các nguyên tắc tốt nhất.

Authentication HOC

Một trường hợp sử dụng phổ biến của HOCs là xử lý xác thực. Dưới đây là một ví dụ:

function withAuth(WrappedComponent) {
return function(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false);

useEffect(() => {
// Kiểm tra nếu người dùng đã xác thực
const token = localStorage.getItem('token');
setIsAuthenticated(!!token);
}, []);

if (!isAuthenticated) {
return <Redirect to="/login" />;
}

return <WrappedComponent {...props} />;
}
}

// Sử dụng
const ProtectedComponent = withAuth(SensitiveDataComponent);

HOC này kiểm tra xem người dùng có được xác thực hay không trước khi hiển thị component được bao bọc. Nếu không, nó sẽ chuyển hướng đến trang đăng nhập.

Logging HOC

Một ứng dụng hữu ích khác là thêm日志 vào các component:

function withLogging(WrappedComponent) {
return function(props) {
useEffect(() => {
console.log(`Component ${WrappedComponent.name} mounted`);
return () => console.log(`Component ${WrappedComponent.name} unmounted`);
}, []);

return <WrappedComponent {...props} />;
}
}

// Sử dụng
const LoggedComponent = withLogging(MyComponent);

HOC này日志 khi component được装载 và gỡ bỏ, điều này rất hữu ích cho việc gỡ lỗi.

Best Practices and Considerations

Khi làm việc với HOCs, hãy nhớ những mẹo sau:

  1. Don't Mutate the Original Component: Luôn kết hợp component gốc với các chức năng mới.
  2. Pass Unrelated Props Through: Đảm bảo truyền qua bất kỳ prop nào không liên quan đến HOC.
  3. Maximize Composability: HOCs nên có thể kết hợp với các HOC khác.
  4. Wrap the Display Name for Easy Debugging: Sử dụng React.displayName để đặt tên rõ ràng cho HOC trong React DevTools.

Dưới đây là bảng tóm tắt một số mẫu HOC phổ biến:

Mẫu Mô tả Ví dụ Trường hợp sử dụng
Props Proxy Thao tác với props Thêm/chỉnh sửa props
Inheritance Inversion Mở rộng chu kỳ sống của component Thêm日志 vào phương thức chu kỳ sống
Render Highjacking Kiểm soát đầu ra của render Hiển thị điều kiện
State Abstraction Quản lý và cung cấp trạng thái Lấy và cung cấp dữ liệu

Nhớ rằng, Higher-Order Components là một công cụ mạnh mẽ trong bộ công cụ React của bạn, nhưng chúng không phải lúc nào cũng là giải pháp tốt nhất. Với sự ra đời của Hooks trong React, một số trường hợp sử dụng của HOCs có thể được giải quyết một cách tinh tế hơn với hooks tùy chỉnh. Luôn xem xét nhu cầu cụ thể của ứng dụng của bạn khi quyết định có nên sử dụng HOCs, hooks hay các mẫu khác hay không.

Và đây là tất cả, các bạn! Bạn đã chính thức bước những bước đầu tiên vào thế giới của Higher-Order Components trong React. Hãy thực hành các khái niệm này, thử nghiệm với HOCs của riêng bạn, và sớm bạn sẽ có khả năng kết hợp component như một nghệ sĩ chỉ huy dàn nhạc. Chúc các bạn viết mã vui vẻ!

Credits: Image by storyset