Facebook Pixel

Render Props pattern là gì? Hướng dẫn sử dụng Render Props

03 Dec, 2024

Tran Thuy Vy

Frontend Developer

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

Render Props pattern là gì? Hướng dẫn sử dụng Render Props

Mục Lục

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.

JSX
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:

JSX
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ủa MouseTracker 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.

JSX
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.

JSX
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 renderKelvinrenderFahrenheit 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.

JSX
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 renderKelvinrenderFahrenheit, 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ị.

JSX
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ể.

JSX
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ái expanded và cung cấp hàm toggleExpand.
  • Trong App, bạn sử dụng Expandable và truyền vào một hàm render thông qua children. 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.

JSX
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

Lập trình backend expressjs

xây dựng hệ thống microservices
  • Kiến trúc Hexagonal và ứng dụngal font-
  • TypeScript: OOP và nguyên lý SOLIDal font-
  • Event-Driven Architecture, Queue & PubSubal font-
  • Basic scalable System Designal font-

Đăng ký nhận thông báo

Đừng bỏ lỡ những bài viết thú vị từ 200Lab