Tiếng Việt

Hướng dẫn toàn diện về React Portals, bao gồm các trường hợp sử dụng, cách triển khai, lợi ích và các phương pháp hay nhất để hiển thị nội dung bên ngoài cấu trúc thành phần tiêu chuẩn.

React Portals: Hiển thị Nội dung Bên ngoài Cây Thành phần

React portals cung cấp một cơ chế mạnh mẽ để hiển thị các thành phần con vào một nút DOM tồn tại bên ngoài hệ thống phân cấp DOM của thành phần cha. Kỹ thuật này vô giá trong nhiều kịch bản khác nhau, chẳng hạn như modals, tooltips, và các tình huống bạn cần kiểm soát chính xác vị trí và thứ tự xếp chồng của các phần tử trên trang.

React Portals là gì?

Trong một ứng dụng React thông thường, các thành phần được hiển thị trong một cấu trúc phân cấp nghiêm ngặt. Thành phần cha chứa các thành phần con, và cứ thế tiếp tục. Tuy nhiên, đôi khi bạn cần thoát ra khỏi cấu trúc này. Đây là lúc React portals phát huy tác dụng. Một portal cho phép bạn hiển thị nội dung của một thành phần vào một phần khác của DOM, ngay cả khi phần đó không phải là hậu duệ trực tiếp của thành phần trong cây React.

Hãy tưởng tượng bạn có một thành phần modal cần được hiển thị ở cấp cao nhất của ứng dụng, bất kể nó được hiển thị ở đâu trong cây thành phần. Nếu không có portals, bạn có thể cố gắng đạt được điều này bằng cách sử dụng định vị tuyệt đối và z-index, điều này có thể dẫn đến các vấn đề về kiểu dáng phức tạp và xung đột tiềm ẩn. Với portals, bạn có thể trực tiếp hiển thị nội dung của modal vào một nút DOM cụ thể, chẳng hạn như một phần tử "modal-root" chuyên dụng, đảm bảo nó luôn được hiển thị ở cấp độ chính xác.

Tại sao nên sử dụng React Portals?

React Portals giải quyết một số thách thức phổ biến trong phát triển web:

Cách triển khai React Portals

Sử dụng React Portals rất đơn giản. Dưới đây là hướng dẫn từng bước:

  1. Tạo một nút DOM: Đầu tiên, tạo một nút DOM nơi bạn muốn hiển thị nội dung portal. Điều này thường được thực hiện trong tệp `index.html` của bạn. Ví dụ:
    <div id="modal-root"></div>
  2. Sử dụng `ReactDOM.createPortal()`: Trong thành phần React của bạn, sử dụng phương thức `ReactDOM.createPortal()` để hiển thị nội dung vào nút DOM đã tạo. Phương thức này nhận hai đối số: nút React (nội dung bạn muốn hiển thị) và nút DOM nơi bạn muốn hiển thị nó.
    import ReactDOM from 'react-dom';
    
    function MyComponent() {
      return ReactDOM.createPortal(
        <div>Nội dung này được hiển thị trong modal-root!</div>,
        document.getElementById('modal-root')
      );
    }
    
    export default MyComponent;
  3. Hiển thị Thành phần: Hiển thị thành phần chứa portal như bạn làm với bất kỳ thành phần React nào khác.
    function App() {
      return (
        <div>
          <h1>Ứng dụng của tôi</h1>
          <MyComponent />
        </div>
      );
    }
    
    export default App;

Trong ví dụ này, nội dung bên trong `MyComponent` sẽ được hiển thị bên trong phần tử `modal-root`, mặc dù `MyComponent` được hiển thị bên trong thành phần `App`.

Ví dụ: Tạo một Thành phần Modal với React Portals

Hãy tạo một thành phần modal hoàn chỉnh bằng React Portals. Ví dụ này bao gồm kiểu dáng cơ bản và chức năng để mở và đóng modal.

import React, { useState } from 'react';
import ReactDOM from 'react-dom';

const modalRoot = document.getElementById('modal-root');

function Modal({ children, onClose }) {
  const [isOpen, setIsOpen] = useState(true);

  const handleClose = () => {
    setIsOpen(false);
    onClose();
  };

  if (!isOpen) return null;

  return ReactDOM.createPortal(
    <div className="modal-overlay">
      <div className="modal">
        <div className="modal-content">
          {children}
        </div>
        <button onClick={handleClose}>Đóng</button>
      </div>
    </div>,
    modalRoot
  );
}

function App() {
  const [showModal, setShowModal] = useState(false);

  const handleOpenModal = () => {
    setShowModal(true);
  };

  const handleCloseModal = () => {
    setShowModal(false);
  };

  return (
    <div>
      <h1>Ứng dụng của tôi</h1>
      <button onClick={handleOpenModal}>Mở Modal</button>
      {showModal && (
        <Modal onClose={handleCloseModal}>
          <h2>Nội dung Modal</h2>
          <p>Đây là nội dung của modal.</p>
        </Modal>
      )}
    </div>
  );
}

export default App;

Trong ví dụ này:

Bạn cũng cần thêm một số kiểu CSS vào các lớp `modal-overlay` và `modal` để định vị modal một cách chính xác trên màn hình. Ví dụ:

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal {
  background-color: white;
  padding: 20px;
  border-radius: 5px;
}

.modal-content {
  margin-bottom: 10px;
}

Xử lý Sự kiện với Portals

Một cân nhắc quan trọng khi sử dụng portals là cách xử lý các sự kiện. Sự kiện nổi bọt (event bubbling) hoạt động khác với portals so với các thành phần React tiêu chuẩn.

Khi một sự kiện xảy ra trong một portal, nó sẽ nổi bọt lên qua cây DOM như bình thường. Tuy nhiên, hệ thống sự kiện của React coi portal như một nút React thông thường, có nghĩa là các sự kiện cũng sẽ nổi bọt lên qua cây thành phần React chứa portal đó.

Điều này đôi khi có thể dẫn đến hành vi không mong muốn nếu bạn không cẩn thận. Ví dụ, nếu bạn có một trình xử lý sự kiện trên một thành phần cha mà chỉ nên được kích hoạt bởi các sự kiện bên trong thành phần đó, nó cũng có thể được kích hoạt bởi các sự kiện bên trong portal.

Để tránh những vấn đề này, bạn có thể sử dụng phương thức `stopPropagation()` trên đối tượng sự kiện để ngăn sự kiện nổi bọt lên xa hơn. Ngoài ra, bạn có thể sử dụng các sự kiện tổng hợp (synthetic events) của React và hiển thị có điều kiện để kiểm soát khi nào các trình xử lý sự kiện được kích hoạt.

Đây là một ví dụ về việc sử dụng `stopPropagation()` để ngăn một sự kiện nổi bọt lên thành phần cha:

function MyComponent() {
  const handleClick = (event) => {
    event.stopPropagation();
    console.log('Đã nhấp vào bên trong portal!');
  };

  return ReactDOM.createPortal(
    <div onClick={handleClick}>Nội dung này được hiển thị trong portal.</div>,
    document.getElementById('portal-root')
  );
}

Trong ví dụ này, việc nhấp vào nội dung bên trong portal sẽ kích hoạt hàm `handleClick`, nhưng sự kiện sẽ không nổi bọt lên bất kỳ thành phần cha nào.

Các phương pháp tốt nhất khi sử dụng React Portals

Dưới đây là một số phương pháp tốt nhất cần ghi nhớ khi làm việc với React Portals:

Các giải pháp thay thế cho React Portals

Mặc dù React Portals là một công cụ mạnh mẽ, có những cách tiếp cận khác bạn có thể sử dụng để đạt được kết quả tương tự. Một số giải pháp thay thế phổ biến bao gồm:

Việc lựa chọn cách tiếp cận nào phụ thuộc vào các yêu cầu cụ thể của ứng dụng của bạn và độ phức tạp của các phần tử UI bạn đang cố gắng tạo ra. Portals thường là lựa chọn tốt nhất khi bạn cần kiểm soát chính xác vị trí và thứ tự xếp chồng của các phần tử và muốn tránh xung đột CSS.

Những lưu ý toàn cầu

Khi phát triển ứng dụng cho đối tượng toàn cầu, điều cần thiết là phải xem xét các yếu tố như bản địa hóa, khả năng tiếp cận và sự khác biệt văn hóa. React Portals có thể đóng một vai trò trong việc giải quyết những cân nhắc này:

Bằng cách tính đến những cân nhắc toàn cầu này, bạn có thể tạo ra các ứng dụng toàn diện và thân thiện với người dùng hơn cho một đối tượng đa dạng.

Kết luận

React Portals là một công cụ mạnh mẽ và linh hoạt để hiển thị nội dung bên ngoài cây thành phần tiêu chuẩn. Chúng cung cấp một giải pháp gọn gàng và tinh tế cho các mẫu UI phổ biến như modals, tooltips và popovers. Bằng cách hiểu cách portals hoạt động và tuân theo các phương pháp tốt nhất, bạn có thể tạo ra các ứng dụng React linh hoạt, dễ bảo trì và dễ tiếp cận hơn.

Hãy thử nghiệm với portals trong các dự án của riêng bạn và khám phá nhiều cách chúng có thể đơn giản hóa quy trình phát triển UI của bạn. Hãy nhớ xem xét việc xử lý sự kiện, khả năng tiếp cận và các cân nhắc toàn cầu khi sử dụng portals trong các ứng dụng sản phẩm.

Bằng cách làm chủ React Portals, bạn có thể nâng cao kỹ năng React của mình lên một tầm cao mới và xây dựng các ứng dụng web phức tạp và thân thiện hơn với người dùng cho đối tượng toàn cầu.