iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
Modern Web

30 days of React 系列 第 23

Day 23 - prop drilling 和 context

  • 分享至 

  • xImage
  •  

今天來學習如何使用 context 來解決 prop drilling 的問題。今天會學習的內容為:

  • 了解什麼是 prop drilling
  • context 的操作方法

什麼是 prop drilling

prop drilling 就像生活中的水管系統一樣。想像一個大樓,裡面有好多層樓,每層樓都需要用水。但是水源卻在最頂層,所以每一層都需要一根水管來把水從頂樓運到下面。

這些水管就像是連接一樓、二樓、三樓的管道,如果一樓的人需要水,他得透過每一層的水管才能得到水。這就是 prop drilling,資料或訊息必須一層一層地傳遞,即使中間的樓層並不真的需要這些訊息。這樣的過程可能會讓程式碼變得複雜,就像在房子中需要多根水管一樣。而且如果樓層很多,水流(效能)可能會變慢,因為水要經過好多根水管。

(圖片出自 React 官方文件)

Context 的功能

Context 在 React 當中是一個用於共享狀態和數據的機制,它允許資料傳遞到應用程式中的多個元件,而不需要將資料通過 props 一層層地傳遞。先來看一個例子,假設我們的應用程式是在不同的 section 中會有不同的 heading 大小。


初步的詳細的程式碼:https://codesandbox.io/s/ycy7tf?file=/Heading.js

按照之前的學習內容我們能透過 props 去進行這些操作:

<Section>
  <Heading level={4}>Sub-sub-heading</Heading>
  <Heading level={4}>Sub-sub-heading</Heading>
  <Heading level={4}>Sub-sub-heading</Heading>
</Section>

但這樣子可能造成的問題就是剛剛提到的 prop drilling。要解決這樣問題的話,理想應該是要像這樣直接從父元件Section來控制,像這樣:

<Section level={4}>
  <Heading>Sub-sub-heading</Heading>
  <Heading>Sub-sub-heading</Heading>
  <Heading>Sub-sub-heading</Heading>
</Section>

我們可以透過 Context 達成這樣的目的。讓我們來學習該如何操作:

在操作前先來看看會用到哪些檔案:

  • App.js
  • Section.js
  • Heading.js
  • LevelContext.js

Step 1 : 創建 Context

LevelContext.js檔案中我們需要建立希望共享的資料,並將資料 export。

import { createContext } from "react";

export const LevelContext = createContext(1);

這邊的 createContext()的唯一參數為預設值,我們設定這邊設定1是考量到最大的 level Heading。

Step 2: 使用 Context

創建好 context 後,我們像在可以開始使用了。但首先需要先引入useContext

需要先在管理 heading 的檔案中引入:

import { useContext } from "react";
import { LevelContext } from "./LevelContext.js";

原先不使用useContext的操作方式也就是在Heading 元件 props 直接去讀取 level。

但這邊我們將Heading元件中的level屬性(prop)的讀取方式改為使用 React 的useContext hook 從LevelContext中獲取。

也就會是這樣子:

export default function Heading({ children }) {
  const level = useContext(LevelContext);
  // ...
}

我們這樣的操作也就是告訴了 React:Heading 元件想要讀取LevelContext

因爲沒有在Heading中使用 level props 所以在 JSX 當中我們也可以改變成以下的寫法:

<Section level={4}>
  <Heading>Sub-sub-heading</Heading>
  <Heading>Sub-sub-heading</Heading>
  <Heading>Sub-sub-heading</Heading>
</Section>

在這個情境下,Heading 元件並不是直接從 LevelContext 中拿資料,而是透過 Section 父元件間接地獲取 level 資料。

這樣子就完成了第二步驟,已經把「接收」資料的路徑建置完成了。接下來要來把「提供」資料的路徑也建置出來。

Step 3: 提供 Context

目前 Section 元件在渲染的是它的 children,像這樣:

export default function Section({ children }) {
  return <section className="section">{children}</section>;
}

但這樣子沒有辦法向子元件提供資料,所以需要調整成用 context provider 去包覆子元件(就是{children})。

import { LevelContext } from "./LevelContext.js";

export default function Section({ level, children }) {
  return (
    <section className="section">
      <LevelContext.Provider value={level}>{children}</LevelContext.Provider>
    </section>
  );
}

這邊的程式碼,我們操作了三件事:

(1) import LevelContext

(2) 以 context provider LevelContext.Provider{children}包覆起來

(3) 將 value prop 設定為{level}

這樣子就能達到我們一開始想要的將資料透過父元件來傳遞了。

參考資料

  • React 官方文件 - Passing Data Deeply with Context

上一篇
Day 22 - Reducer 的應用
下一篇
Day 24 - React 基礎實作練習:輸入授權碼
系列文
30 days of React 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言