文章

React Fragments 與 Portals

React Fragments 與 Portals

在 React 開發中,FragmentsPortals 是兩個有助於控制 DOM 結構的強大工具。Fragments 允許在不增加額外 DOM 節點的情況下返回多個元素,而 Portals 則可以讓你將組件的子元素渲染到 DOM 樹中的不同位置。


1. Fragments (片段)

1.1 什麼是 Fragment?

在 React 中,一個組件的 render 方法或函數組件只能返回一個單一的 DOM 節點。然而,當我們希望返回多個元素而不希望額外的包裹元素影響 DOM 結構時,可以使用 Fragment 來包裹這些元素。Fragments 本身不會在 DOM 中創建任何多餘的節點。

1.2 基本語法

使用 React.Fragment 或其簡寫 <></>,可以在不增加多餘的 DOM 標籤的情況下返回多個子元素。

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';

function MyComponent() {
  return (
    <React.Fragment>
      <h1>標題</h1>
      <p>這是一段文字。</p>
    </React.Fragment>
  );
}

export default MyComponent;

簡寫形式:

1
2
3
4
5
6
7
8
function MyComponent() {
  return (
    <>
      <h1>標題</h1>
      <p>這是一段文字。</p>
    </>
  );
}

1.3 使用場景

  1. 避免多餘的包裹元素:在不需要額外 DOM 節點的情況下組織多個元素,避免引入不必要的 <div> 或其他包裹元素。

  2. 提升渲染性能:因為 Fragment 不會創建額外的 DOM 節點,它有助於優化渲染性能,特別是在需要渲染大量元素時。


2. Portals (入口)

2.1 什麼是 Portal?

React Portals 提供了一個將子元素渲染到父組件 DOM 節點之外的方式。換句話說,Portal 允許你將組件的部分 UI 渲染到 DOM 樹中的另一個節點,而不是按照 React 組件層次結構進行渲染。

2.2 基本語法

Portals 使用 ReactDOM.createPortal 來實現,它接受兩個參數:

  1. 子元素:你想要渲染的子組件。
  2. DOM 節點:目標 DOM 節點,即你希望渲染到的位置。
1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDOM from 'react-dom';

function MyPortalComponent() {
  return ReactDOM.createPortal(
    <div>這是從 Portal 渲染出來的內容。</div>,
    document.getElementById('portal-root') // 指定渲染位置
  );
}

export default MyPortalComponent;

在上例中,我們將一個 div 元素渲染到了具有 id="portal-root" 的 DOM 節點中,這個節點可能與 React 應用的根節點是平行的。

2.3 使用場景

  1. 模態窗口 (Modal):模態窗口通常需要渲染在頂層,脫離當前組件的 DOM 層次結構。使用 Portals 可以確保模態窗口不會受到父組件的 overflowz-index 的影響。

  2. 工具提示 (Tooltip):像 Tooltip 這樣的浮動 UI 元素,也經常需要在 DOM 的特殊位置渲染,以避免被其他元素遮蓋。

  3. 全局通知或對話框:這些元素經常需要渲染在應用的最外層,使用 Portals 可以更容易管理它們。

2.4 範例:模態窗口

1
2
3
4
5
6
7
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
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

function Modal({ children }) {
  return ReactDOM.createPortal(
    <div className="modal">
      <div className="modal-content">
        {children}
      </div>
    </div>,
    document.getElementById('modal-root')
  );
}

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

  const handleToggleModal = () => {
    setShowModal(!showModal);
  };

  return (
    <div>
      <button onClick={handleToggleModal}>
        {showModal ? '隱藏模態窗口' : '顯示模態窗口'}
      </button>
      {showModal && <Modal>這是一個模態窗口</Modal>}
    </div>
  );
}

export default App;

在這個範例中,我們使用 Portal 將模態窗口渲染到了 id="modal-root" 的 DOM 節點中,這樣即使它在父組件之外,它也能正確地顯示和運行。


3. Fragments 與 Portals 的區別

  • Fragments:用於組織 JSX 結構,在不引入多餘 DOM 節點的情況下返回多個元素,從而保持 DOM 結構的簡潔。
  • Portals:用於將子元素渲染到不同於父組件 DOM 結構的節點,適合處理模態窗口、浮動 UI 等需要脫離組件層次的元素。

4. 總結

  • Fragments 使你可以返回多個元素而不引入額外的 DOM 節點,適合在不需要包裹的情況下渲染多個子元素。
  • Portals 讓你可以將子組件渲染到父組件之外的 DOM 節點,這對於模態窗口、工具提示等需要脫離常規 DOM 結構的情況非常有用。

使用 Fragments 和 Portals 可以幫助你靈活管理應用中的 DOM 結構,同時提高性能和靈活性。

本文章以 CC BY 4.0 授權