Trang chủ Kiến Thức Công Nghệ So sánh giữa HOCs, Render Props và Hooks.
Công Nghệ

So sánh giữa HOCs, Render Props và Hooks.

Chia sẻ
So sánh giữa HOCs, Render Props và Hooks.
Chia sẻ

Chủ đề hôm nay mình muốn chia sẻ với các bạn về ba pattern phổ biến trong React: Higher-Order Components (HOCs), Render Props, và Hooks. Đây đều là những công cụ giúp bạn tái sử dụng logic giữa các component. Hãy cùng tìm hiểu từng phương pháp, so sánh chúng, và xem xét khi nào nên sử dụng phương pháp nào. Mình cũng sẽ cung cấp các ví dụ và đoạn code cụ thể để minh họa sự khác nhau giữa chúng.

1. So sánh tổng quan

Higher-Order Components (HOCs)Render PropsHooks
Cơ chế triển khaiHàm nhận component và trả về component mớiComponent nhận prop là một hàm renderSử dụng trong functional components với hooks
Tái sử dụng logicBằng cách bọc componentBằng cách sử dụng hàm renderBằng cách tạo custom hooks
Độ phức tạp codeCó thể cao khi lồng nhiều HOCCó thể gây lồng nhau trong JSXThường ngắn gọn và dễ đọc
Xung đột tênCó thể xảy ra, cần cẩn thậnTránh được xung đột tênTránh được xung đột tên
Khả năng debugKhó hơn do nhiều lớp bọcTrung bình, có thể khó với nhiều render propsDễ hơn, stack trace rõ ràng
Hỗ trợHoạt động với cả class và functional componentsHoạt động với cả class và functional componentsChỉ hoạt động với functional components
Hiệu suấtCó thể ảnh hưởng nếu không tối ưuCó thể ảnh hưởng do tạo hàm mới mỗi lần renderTối ưu hơn, hooks được React tối ưu
Độ phổ biến hiện tạiÍt phổ biến hơn do sự xuất hiện của hooksVẫn được sử dụng trong một số trường hợpRất phổ biến và được khuyến nghị

2. Cơ chế hoạt động

Trong phần này, mình sẽ mô tả cơ chế hoạt động của từng phương pháp, kèm theo mô tả về sơ đồ minh họa để giúp bạn hình dung rõ hơn.

2.1 HOCs

HOC là hàm nhận vào component và trả về một component mới, cho phép bạn thêm hoặc thay đổi hành vi của component gốc mà không cần sửa đổi nó.

Cơ chế hoạt động của HOC
  • Original Component (ComponentA): component gốc mà bạn muốn mở rộng hoặc thêm logic mới.
  • HOC Function (withExtraLogic): HOC nhận vào ComponentA làm tham số.
  • Enhanced Component (ComponentB): kết quả của HOC sau khi áp dụng logic bổ sung lên ComponentA.
  • Rendered Output: giao diện được render ra sau khi sử dụng ComponentB.

2.2 Render props

Sử dụng prop có giá trị là hàm để kiểm soát những gì được render.

Cơ chế hoạt động render props
  • Component cha (DataProvider): chứa logic chung và gọi hàm render prop.
  • Hàm render prop (children): hàm được truyền vào DataProvider để quyết định render gì dựa trên dữ liệu nhận được.
  • Component con (ChildComponent): được render thông qua hàm render prop, sử dụng dữ liệu từ DataProvider.

2.3 Hook

Sử dụng các hàm hook để thêm state và các tính năng React khác vào functional components.

Cơ chế hoạt động Hook
  • Functional Component (MyComponent): Dùng hooks để quản lý state và side effects.
  • Custom Hook (useCustomLogic): chứa logic chung, có thể được tái sử dụng.
  • State và Side Effects: được quản lý trong MyComponent thông qua hooks.

3. Ví dụ và code

Mình sẽ lấy ví dụ: get data từ API và hiển thị trong component.

3.1 Triển khai với HOCs (Higher-Order Components)

JSX

import React from 'react';

function withDataFetching(WrappedComponent, dataSource) {
  return class extends React.Component {
    state = {
      data: [],
      isLoading: false,
      error: null,
    };

    componentDidMount() {
      this.setState({ isLoading: true });
      fetch(dataSource)
        .then(response => response.json())
        .then(data => this.setState({ data, isLoading: false }))
        .catch(error => this.setState({ error, isLoading: false }));
    }

    render() {
      return (
        <WrappedComponent
          {...this.props}
          data={this.state.data}
          isLoading={this.state.isLoading}
          error={this.state.error}
        />
      );
    }
  };
}

export default withDataFetching;
  • Sử dụng trong UserList.js

JSX

import React from 'react';
import withDataFetching from './withDataFetching';

function UserList({ data, isLoading, error }) {
  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error fetching data!</p>;
  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default withDataFetching(UserList, 'https://api.example.com/users');

withDataFetching bọc UserList, thêm logic lấy dữ liệu và truyền dữ liệu thông qua props.

Bạn có thể thấy được những điểm hạn chế khi sử dụng HOCs:

  • Có thể xảy ra xung đột tên với các props khác.
  • Code có thể khó debug khi bạn lồng nhiều HOC.
  • Không linh hoạt trong việc kiểm soát render.

3.2 Triển khai với Render Props

JSX

import React from 'react';

class DataFetcher extends React.Component {
  state = {
    data: [],
    isLoading: false,
    error: null,
  };

  componentDidMount() {
    const { url } = this.props;
    this.setState({ isLoading: true });
    fetch(url)
      .then(response => response.json())
      .then(data => this.setState({ data, isLoading: false }))
      .catch(error => this.setState({ error, isLoading: false }));
  }

  render() {
    return this.props.children({
      data: this.state.data,
      isLoading: this.state.isLoading,
      error: this.state.error,
    });
  }
}

export default DataFetcher;
  • Sử dụng Render Props trong UserList.js

JSX

import React from 'react';
import DataFetcher from './DataFetcher';

function UserList() {
  return (
    <DataFetcher url="https://api.example.com/users">
      {({ data, isLoading, error }) => {
        if (isLoading) return <p>Loading...</p>;
        if (error) return <p>Error fetching data!</p>;
        return (
          <ul>
            {data.map(user => (
              <li key={user.id}>{user.name}</li>
            ))}
          </ul>
        );
      }}
    </DataFetcher>
  );
}

export default UserList;
  • DataFetcher chứa logic để lấy dữ liệu và gọi hàm render prop với dữ liệu đó.
  • UserList sử dụng render prop để quyết định render gì dựa trên dữ liệu nhận được.

Những điểm hạn chế khi sử dụng:

  • Sử dụng sẽ dẫn đến việc lồng nhau trong JSX, làm code trở nên khó đọc.
  • Mỗi lần render tạo ra một function mới, có thể ảnh hưởng đến hiệu suất.

3.3 Triển khai với Hooks

JSX

import { useState, useEffect } from 'react';

function useDataFetching(dataSource) {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    setIsLoading(true);
    fetch(dataSource)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setIsLoading(false);
      })
      .catch(error => {
        setError(error);
        setIsLoading(false);
      });
  }, [dataSource]);

  return { data, isLoading, error };
}

export default useDataFetching;
  • Sử dụng Hooks trong UserList.js

JSX

import React from 'react';
import useDataFetching from './useDataFetching';

function UserList() {
  const { data, isLoading, error } = useDataFetching('https://api.example.com/users');

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error fetching data!</p>;
  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UserList;
  • useDataFetching là custom hook chứa logic để lấy dữ liệu.
  • UserList gọi hook này và nhận về dữ liệu, sau đó render trực tiếp.

Những điểm hạn chế của hooks:

  • Không thể sử dụng hooks trong class components.
  • Phải tuân theo các quy tắc như không gọi hooks trong vòng lặp, điều kiện, hoặc hàm lồng nhau.

Từ những phần trên mình có thể đưa ra kết luận như thế này:

  1. Hooks giải quyết hạn chế:
  • Tránh xung đột tên: hooks không cần bọc component hoặc truyền props
  • Code ngắn gọn hơn: không có lồng nhau trong JSX như Render Props.
  • Dễ debug hơn: không có nhiều lớp bọc như HOCs.
  1. Render Props giải quyết hạn chế:
  • Tránh xung đột tên: do không truyền thêm props vào component con.
  • Kiểm soát linh hoạt cách render: Component con quyết định render gì dựa trên dữ liệu nhận được.
  1. HOCs giải quyết hạn chế:
  • Tránh lồng nhau trong JSX: HOCs không làm tăng mức độ lồng nhau như Render Props.
  • Có thể sử dụng với class components: HOCs hoạt động với cả class và functional components, trong khi Hooks chỉ hoạt động với functional components.

4. Khi nào nên sử dụng phương pháp nào?

4.1 Sử dụng Hooks khi

  • Bạn đang sử dụng React phiên bản 16.8 trở lên.
  • Bạn làm việc với functional components.
  • Bạn muốn code ngắn gọn, dễ đọc và dễ bảo trì.
  • Bạn muốn tránh xung đột tên và lồng nhau trong JSX.

4.2 Sử dụng Render Props khi

  • Bạn cần kiểm soát linh hoạt cách render của component con.
  • Bạn làm việc với class components hoặc cần tương thích với code cũ.
  • Bạn muốn tránh xung đột tên mà HOCs có thể gây ra.

4.4 Sử dụng HOCs khi

  • Bạn làm việc với class components và không thể sử dụng Hooks.
  • Bạn cần tái sử dụng logic chung mà không muốn thay đổi cấu trúc component hiện có.
  • Bạn muốn tránh lồng nhau trong JSX mà Render Props có thể gây ra.

5. Kết luận

Trong React, việc lựa chọn giữa Higher-Order Components, Render Props, và Hooks phụ thuộc vào hoàn cảnh cụ thể của dự án và nhu cầu của bạn. Mỗi phương pháp đều có ưu điểm và hạn chế riêng.

Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về sự khác nhau giữa HOCs, Render Props và Hooks, cũng như cách áp dụng chúng trong dự án của mình.

Các bài viết liên quan:

  • Render Props pattern là gì? Hướng dẫn sử dụng Render Props
  • HOCs Pattern là gì? Hướng dẫn triển khai Hocs Pattern trong dự án React
  • Hooks Pattern là gì? Hướng dẫn áp dụng Hooks Pattern trong dự án React
  • React Toastify là gì? Hướng dẫn sử dụng Toast Notification với React Toastify
Bài viết cùng chuyên mục
Tối ưu ứng dụng với cấu trúc dữ liệu cơ bản và bitwise
Công Nghệ

Tối ưu ứng dụng với cấu trúc dữ liệu cơ bản và bitwise

Trong bài viết này, 200Lab sẽ chia sẻ những trường hợp dễ...

Công Nghệ

So sánh Flutter vs React Native: Framework nào đáng học năm 2021

Điểm chung của Flutter, React Native đều là Cross-platform Mobile, build native...

HTTP/2 là gì? So sánh HTTP/2 và HTTP/1
Công Nghệ

HTTP/2 là gì? So sánh HTTP/2 và HTTP/1

Từ khi Internet ra đời, sự phát triển về các giao thức...

Upload File từ Frontend đến Backend mà rất nhiều bạn vẫn đang làm sai!!
Công Nghệ

Upload File từ Frontend đến Backend mà rất nhiều bạn vẫn đang làm sai!!

1. Client encode file (base64) rồi gởi về backend 200Lab đã từng...

Công Nghệ

React Native – Hướng dẫn làm việc với Polyline và Animated-Polyline trên Map

Vẽ đường đi trên bản đồ là một nghiệp vụ vô cùng...

Công Nghệ

Hybrid App và Native App: Những khác biệt to lớn

Bất cứ khi nào một công ty quyết định làm ứng dụng...

Web/System Architecture 101 – Kiến trúc web/hệ thống cơ bản cho người mới
Công Nghệ

Web/System Architecture 101 – Kiến trúc web/hệ thống cơ bản cho người mới

Đây là một kiến trúc cơ bản mà bất kì một người...

Công Nghệ

Tư duy kiến trúc thông qua các trò chơi mà rất nhiều bạn không biết

Tư duy kiến trúc là gì? Tư duy kiến trúc có thể...