useContext

為了解決 Prop drilling 的問題

Alan Huang
6 min readJan 30, 2022

問題:

什麼是 Prop drilling?

一般來說,React 傳資料的方式都是一層一層把資料 props 傳到子層的,就算第二層、第三層根本沒用到這資料,但為了傳到最底層,每一層都還是必須要傳 props,就會使得 state, props 管理變得複雜。

另外一個常見的問題就是,子組件的 state 需傳遞到其他層的子組件使用。

解決方法:

提供一個集中管理 state 的地方:React Context API、Redux。

起手式:

  1. 創建 Context

2. Provider 提供 Context

3. Consumer 使用 Context(兩種方式)

render props pattern (Class component & Functional Component)
useContext hook (Functional Component)

注意:

  • React Context API 沒有提供一個專門的方法來更新 context,所以通常會搭配 useState 或是 useReducer 所提供的 updated function 寫成一個function。
建立一個切換背景主色調的 context
將 context 透過 Provider 引入至整個組件
使用 useContext 獲取 state
App 內的子組件也可使用 useContext 獲取 state
  • 當 Provider 的 value 改變,所有 Consumer 會被通知並 re-render(此為 Context 的一大缺點) 範例:useContext — Counter
    改善方法:拆分 Context 各別管理
const App = ({ user, theme, themeToggle }) => {
return (
<ThemeProvider value={{ theme, themeToggle }}>
<UserProvider value={{ user }}>
<HomePage theme={theme} themeToggle={themeToggle}/>
<Page user={user}/>
</UserProvider>
</ThemeProvider>
);
};
  • 不是每次都要使用 context API,先想想看有沒有更好的方式重構組件
// [App] 
export default function App({ user }) {
const { username, avatarSrc } = user;
return (
<main>
<Navbar username={username} avatarSrc={avatarSrc} />
</main>
);
}
// [Navbar]
function Navbar({ username, avatarSrc }) {
return (
<nav>
<Avatar username={username} avatarSrc={avatarSrc} />
</nav>
);
}
// [Avatar] function Avatar({ username, avatarSrc }) {
return <img src={avatarSrc} alt={username} />;
}

重構組件後

// [App]
export default function App({ user }) {
const { username, avatarSrc } = user;
const avatar = <img src={avatarSrc} alt={username} />;
return (
<main>
<Navbar avatar={avatar} />
</main>
);
}
// [Navbar]
function Navbar({ avatar }) {
return <nav>{avatar}</nav>;
}

重點整理:

  1. React Context API 要解決的問題:複雜的 Prop drilling(建議至少三層以上)。
  2. 使用context的三步驟:
    creating ⇒ providing ⇒ consuming( useContext )
  3. 勿使用useContext取代所有的Props傳遞方法
    並不是說可以用 useContext 去取代原本 props 的方法,而是根據使用情境不同選澤適合的資料傳遞方法。
    畢竟現在 context 有一個很大問題是每當 Provider 資料更新,底下所有 Consumer 組件都會 re-render。而隨意地將 Context 加入後會除了增加程式碼複雜性之外,也會因為過度 re-render 降低效能。 除非 Prop drilling 真的造成很大的困擾再使用 React Context API,不然一般情況下,Props 傳遞2~3層組件是很常見的,不會造成什麼大問題。
  4. 什麼情境適合使用? 不常變動的值適合使用 React Context 傳遞 props。
    a. 偏好語言
    b. 用戶身分驗證
    c. 登入位置更改
    d. 背景主題(light、dark) …
  5. 如何優化? 官網建議的三種避免context更新造成re-render方法
    a. 拆分 Context (👍 推薦)
    b. 分層拆開組件,並搭配 memo (佔React記憶體)
    c. 用 useMemo 包住回傳值 (佔React記憶體)

--

--