React手冊 Hooks 之 useContext
描述
????React 官網(wǎng)對 useContext 的描述原文
????
useContext
is a React Hook that lets you read and subscribe to context from your component.????
useContext
是一個React Hook它可以讓你從組件中讀取和訂閱上下文.
場景
????主要的使用在需要多層嵌套組件之間, 父組件向子組件傳遞數(shù)據(jù)的場景,?如果是單層嵌套組件之間參數(shù)傳遞可以使用?props.
? ? 當(dāng)需要從 App 組件把值透過 Con?組件傳給 Inner 組件時,?如果還是使用 props, 就需要從?Con 組件接收并傳遞給 Inner 這個步驟其實是多余的, 而且會讓?Con 組件變得更復(fù)雜, 使用 useContext 之后, 就可以跳過 Con 組件, 直接在 Inner 組件中獲取到對應(yīng)的值, 并且每次 theme 變化之后, 都會重新渲染 Inner, 將最新的結(jié)果展示.
參數(shù)
context: ReactContext
使用 React.createContext 方法創(chuàng)建出來的 Context 對象, 它本身并不包含任何數(shù)據(jù), 只是一個可供傳遞和從組件中讀取數(shù)據(jù)的對象類型.
返回
Any
useContext 返回組件調(diào)用的 context 對應(yīng)的 value, 也就是?<Context.Provider> 標(biāo)簽上 value 屬性對應(yīng)的值, 如果沒有這個提供者(Provider), 那么就是你在使用 React.createContext 時傳入的初始值, 當(dāng)?context 對應(yīng)的值發(fā)生變化, React 會更新那些使用了該 context 的組件.
用法1
????深層嵌套時, 父級節(jié)點向子集節(jié)點傳遞數(shù)據(jù), 這個關(guān)系不能反過來, 也就是說, 使用 useContext 的節(jié)點, 只會從所有父級節(jié)點中, 找到距離自己最近的一個 <Context.Provider> 標(biāo)簽, 并返回標(biāo)簽中指定的 value.
? ? 父級組件 MyPage:
????子級組件 Form 和 Button:
用法2
????更新 Provider 中指定的 value, 通常情況下我們傳遞的值都需要發(fā)生變化, 以滿足需求, useContext 可以和 useState 結(jié)合在一起使用
????也可以指定對象和函數(shù)類型的值, 將 set 函數(shù)傳遞進(jìn)去, 可以從子組件發(fā)起修改, 或?qū)⒏附M件的函數(shù)傳遞給子組件, 實現(xiàn)子組件給父組件傳值, 例子中的?myAppFunction 方法, 可以在子級中通過 useContext(CurrentUserContext).myAppFunction 獲取并執(zhí)行.
? ? 在上面的例子中, 每次 MyApp 重新渲染時, CurrentUserContext.Provider 也會重新綁定value, 但是傳給 value 的值是一個常量對象 {}, 所以不管 MyApp 的重新渲染是否與?CurrentUserContext.Provider 的 value 有關(guān), 都會讓它檢測到變化,?并通知所有訂閱了CurrentUserContext 的子組件重新渲染, 在大型組件中, 這造成性能浪費.
????使用下面的方式改寫
? ? 這樣使用 useCallback 和 useMemo 改寫之后, 只有當(dāng) CurrentUserContext.Provider 的值 contextValue?發(fā)生變化時, 才會去更新相關(guān)的子組件, 起到優(yōu)化渲染的效果.
用法3
????多個 Context 混用, 在實際開發(fā)的業(yè)務(wù)場景中, 單一的 Context 往往不能滿足需求, 需要多個 Countext 一起使用
????上面例子中, 子組件 UserInfo 可以同時從?ThemeContext 和 CurrentUserContext 兩個Context 中獲取數(shù)據(jù), 但是可能你也發(fā)現(xiàn)了一個問題, 那就是當(dāng) Context 很多的時候, 需要一直嵌套, 這樣讓原本單一的節(jié)點結(jié)構(gòu)變得很復(fù)雜, 可以像下面這個樣改寫
? ? 這樣可以將原本抽象的 <Context.Provider> 抽離出去, 讓?MyApp 組件更加容易理解.
????多個 Context 混用時, 有時會出現(xiàn)同一個 Context 多次嵌套使用的情況, 也就是覆蓋的情況
? ? 在這個例子中, Footer 有兩個父級?<ThemeContext.Provider> 標(biāo)簽, 在 Footer 中使用 useContext(ThemeContext) 時, 獲取的是距離 Footer 最近的?<ThemeContext.Provider>?標(biāo)簽所對應(yīng)的 value, 也就是 "light", 而距離較遠(yuǎn)的 "dark"?就被覆蓋了.
總結(jié)
useContext 主要用于多層嵌套組件之間, 父組件向子組件之間傳遞數(shù)據(jù), 同一組件內(nèi)無法使用 useContext 獲取到最新數(shù)據(jù).
useContext 接收的參數(shù)必須是?React.createContext 創(chuàng)建出來的 Context 對象, 返回<Context.Provider> 綁定的值, 如果沒有找到則返回?React.createContext 創(chuàng)建時的默認(rèn)值, 此默認(rèn)值不可變.
當(dāng) <Context.Provider> 綁定的值發(fā)生變化時, 會通知所有 useContext(Context) 的子組件重新渲染, 并且渲染會跳過 React.memo.
<Context.Provider> 可以傳遞對象和函數(shù), 但是需要配合 useMemo 和 useCallback 對數(shù)據(jù)進(jìn)行合理的緩存, 以免造成不必要的更新.
<Context.Provider> 可以嵌套使用, 相同的 Context 嵌套多次, 距離調(diào)用 useContext 近的?<Context.Provider> 標(biāo)簽會覆蓋距離較遠(yuǎn)的.