在上一篇 對于 Custom React Hooks 一些思考 文章末尾提及了 React Context,那么在本篇中我們將了解一下 React Context 怎么解決狀態共享問題以及一些其它想法,

關于 React Context
提供 官網對于 usecontext 的介紹
const value = useContext(MyContext);
無論組件在組件樹中的深度如何,React Context 都為組件提供資料, 背景關系用于管理全域資料,例如 全域狀態、主題、服務、用戶設定等,
那么我們回到上篇文章提及到的例子吧,如何在父子組件中共享 useBoolean 回傳的狀態呢?

怎樣使用 Context
在給出一份解決方案前,先來說明一下如何使用 Context,步驟也是比較簡單:
- creating the context
- providing the context
- consuming the context
creating the context
首先在 src 目錄下創建一個名為 context 的檔案夾,創建 index.tsx 檔案

通過 createContext 來進行創建操作
import { createContext } from 'react';
const GlobalContext = createContext({})
export default GlobalContext;
providing the context
接下來,在父組件中 providing the context,并且將之前撰寫的 Custom Hooks(useBoolean)回傳的 state 作為 value 值提供給子組件,
import SubApp from './subApp';
import useBoolean from './custom-hooks/useBoolean/index';
import GlobalContext from './context/index'
function App() {
const [state, { toggle, setTrue, setFalse }] = useBoolean(true);
return (
<div>
<p>Effects:{JSON.stringify(state)}</p>
<p>
<button type="button" onClick={() => toggle()}>
Toggle
</button>
<button type="button" onClick={setFalse} style={{ margin: '0 16px' }}>
Set false
</button>
<button type="button" onClick={setTrue}>
Set true
</button>
</p>
// providing the context
<GlobalContext.Provider value={state}><SubApp /></GlobalContext.Provider>
</div>
)
}
export default App;
consuming the context
作為消費方法其實有兩種,一種是通過 useContext(Context),另外一種是通過 Context.Consumer 提供一個特殊組件的渲染函式,
通過 useContext(Context)
子組件代碼撰寫如下:
import { useContext } from 'react'
import GlobalContext from './context/index'
const SubApp = () => {
// useContext
const val = useContext(GlobalContext)
return (
<div>
<p>subEffects:{JSON.stringify(val)}</p>
</div>
)
}
export default SubApp
看起來這種實作方式也是比較簡單的,我們看看效果,當我們點擊 Toggle 按鈕時,發現在父子狀態進行了共享,效果實作,

通過 Context.Consumer
接下里,試一下第二種方式,代碼如下:
import GlobalContext from './context/index'
const SubApp = () => {
return (
<div>
<GlobalContext.Consumer>
{(val) => <p>subEffects:{JSON.stringify(val)}</p>}
</GlobalContext.Consumer>
</div>
)
}
export default SubApp
進行相同的操作,也同樣實作了效果,

拓展知識
經過上述兩個方法的實作,基本上也解決了上篇文章提及的問題,在這里補充一下關于 context 的知識,
對于我們創建的 context,就比如上文我創建的 GlobalContext,它可以擁有任意數量的Consumer,
如果 GlobalContext 值發生變化(通過更改 Context.Provider 的 value 屬性 ),那么所有的 Consumer 都會立即收到通知并重新渲染,
可能文字的表述不是很清楚,那么就以下列圖示來說明(圖畫的有點簡單,湊合下吧 hh)

假設組件 APP 是我們的 Provider,底下組件 ABCE 都是它的 Consumer,那么當我更改 Context.Provider 的 value 屬性時,所有的 Consumer(這里指 ABCE)都會收到通知并重新渲染,
細心的小伙伴可能發現了,不還有個組件 D 沒說嘛,下面就來說明:
如果 Consumer 沒有被包裝在 Context.Provider 中,但仍然嘗試訪問 Context (使用 useContext(Context) 或 <Context.Consumer>),那么 Context 值拿到的是 createContext(defaultValue) 的默認值 ,
const GlobalContext = createContext({})
如上述代碼,就比如之前創建的 GlobalContext,子組件如果被包裝在 GlobalContext.Provider 中,那么獲取的就是更改的 value 值,而如果沒有被包的話,就是獲取的默認值(這里是 {})
Context 該何時何地使用
在上文,我們了解了 Context 使用好處,但我們應該在哪個場景去使用它呢?難道不成就隨便用就好了?會給我們帶來什么問題呢?
我們一步一步來回答上述問題,
首先是我們應該在哪個場景去使用它呢?
一提到共享狀態以及本文中創建的也是 GlobalContext,就想到全域狀態了,
我們可以存放一些全域狀態,比如一些用戶基本資訊,應用的一些配置項(比如認證,基本資訊等),以及服務相關等等,這個就可以根據具體的業務需求來決定了,
但是,在我們使用 Context 不得不考慮一下該怎樣使用它,難道真就隨便用?
就以本文的代碼例子來說,我們通過了兩種方式在父子組件中實作狀態的共享,但是發現沒有,只要包括在 GlobalContext.Provider 其中的子組件,都必須使用 useContext(Context) 和 Context.Consumer 或者其它可能的方式來實作,
如果子組件很多,那這樣層層環扣,豈不是影響的層級會比較多,而且在子組件中就會多增加一行代碼,整個樹結構復雜性就會上升,
當然,如果子樹必須共享狀態或者組件內有大量的計算,那么使用 Context 還是會方便許多,它可以減少一些沒必要的計算,直接共享就完事了,
結尾
那么,本文到此就結束了,你會期待接下來的文章嗎?
我是【一百個Chocolate】,希望在文章中體現自己的思考,然后與大家分享,堅持學習打卡第二篇,我們下期再見,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/305469.html
標籤:其他
