useContext
為了解決 Prop drilling 的問題
6 min readJan 30, 2022
問題:
什麼是 Prop drilling?
一般來說,React 傳資料的方式都是一層一層把資料 props 傳到子層的,就算第二層、第三層根本沒用到這資料,但為了傳到最底層,每一層都還是必須要傳 props,就會使得 state, props 管理變得複雜。
另外一個常見的問題就是,子組件的 state 需傳遞到其他層的子組件使用。
解決方法:
提供一個集中管理 state 的地方:React Context API、Redux。
起手式:
- 創建 Context
2. Provider 提供 Context
3. Consumer 使用 Context(兩種方式)
注意:
- React Context API 沒有提供一個專門的方法來更新 context,所以通常會搭配 useState 或是 useReducer 所提供的 updated function 寫成一個function。
- 當 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>;
}
重點整理:
- React Context API 要解決的問題:複雜的 Prop drilling(建議至少三層以上)。
- 使用context的三步驟:
creating ⇒ providing ⇒ consuming( useContext ) - 勿使用useContext取代所有的Props傳遞方法:
並不是說可以用 useContext 去取代原本 props 的方法,而是根據使用情境不同選澤適合的資料傳遞方法。
畢竟現在 context 有一個很大問題是每當 Provider 資料更新,底下所有 Consumer 組件都會 re-render。而隨意地將 Context 加入後會除了增加程式碼複雜性之外,也會因為過度 re-render 降低效能。 除非 Prop drilling 真的造成很大的困擾再使用 React Context API,不然一般情況下,Props 傳遞2~3層組件是很常見的,不會造成什麼大問題。 - 什麼情境適合使用? 不常變動的值適合使用 React Context 傳遞 props。
a. 偏好語言
b. 用戶身分驗證
c. 登入位置更改
d. 背景主題(light、dark) … - 如何優化? 官網建議的三種避免context更新造成re-render方法:
a. 拆分 Context (👍 推薦)
b. 分層拆開組件,並搭配 memo (佔React記憶體)
c. 用 useMemo 包住回傳值 (佔React記憶體)