您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么封裝React Context Composer”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“怎么封裝React Context Composer”吧!
我是如何一步步封裝一個React Context Composer?
動機
React的狀態(tài)管理方案有很多,比如Redux、Mobx、Recoil等,目前我只體驗過Redux,覺得還是比較笨重一點。因為平時寫Hooks比較多,所以我比較傾向于使用Context Provider配合useContext這個hook來做,這樣也易于狀態(tài)的拆分與組合。這里,我們不討論各家狀態(tài)管理方案的優(yōu)劣,將目光聚焦于在使用Context時遇到的一個多層嵌套的問題。
下圖,是我最近在寫的一個taro + react hooks + ts項目抽離出來的一些代碼。我對一些全局狀態(tài)進行了拆分(拆分的目的是為了減少不必要的重新渲染),然后再把它們嵌套起來。這種寫法讓我回想起了曾經(jīng)被回調(diào)地獄支配的感覺,很難受。因此,我想到了自己去封一個高階組件,從寫法上把結(jié)構(gòu)“扁平化”。
<LoadingContext.Provider value={{ loading, setLoading }}> <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }}> <ThemeContext.Provider value={"light"}> {/* ....more Providers as long as you want */} </ThemeContext.Provider> </UserDataContext.Provider> </LoadingContext.Provider>
最易得的方案
這里,我很快的就寫出了第一種方案,借助reduceRight去完成Provider的嵌套。
這里用reduceRight而不用reduce的原因是,我們更加習(xí)慣從外層到內(nèi)層的書寫順序。
// ContextComposer.tsx import React from 'react'; type IContextComposerProps = { contexts: { context: React.Context<any>; value: any }[]; }; const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => { return ( <> {contexts.reduceRight((child, parent) => { const { context, value } = parent; return <context.Provider value={value}>{child}</context.Provider>; }, children)} </> ); }; export default ContextComposer; // App.tsx <ContextComposer contexts={[ { context: ThemeContext, value: "light" }, { context: UserDataContext, value: { name: "ascodelife", age: 25 } }, { context: LoadingContext, value: { loading, setLoading } }, ]}> { children } </ContextComposer>
實際體驗后發(fā)現(xiàn),雖然說能用是能用,但是開發(fā)體驗差那么一點。它的問題在于,組件入?yún)r傳的value是any類型,這就意味著放棄了ts的靜態(tài)類型檢查。在傳參時,由于不會對value做靜態(tài)類型檢查,敲起代碼來不僅不會有任何代碼提示,也有可能造成一些比較低級的運行時錯誤。差評!
基于React.cloneElement()的改造方案
為了改造上面的這種方案,我翻到了一個比較冷門但好用的函數(shù)—— React.cloneElement()。這個函數(shù)沒有很多需要值得注意的點,主要看一眼它的三個入?yún)?,第一個是parent element,第二個是parent props,第三個是剩余參數(shù)...children,除第一個參數(shù)外,其他都是可選值。
舉個例子:
<!-- 調(diào)用函數(shù) --> React.cloneElement(<div/>,{},<span/>); <!-- 相當(dāng)于創(chuàng)建了這樣一個結(jié)構(gòu) --> <div> <span></span> </div>
那么下面開始改造,reduceRight的架子不動,改一下入?yún)⒌念愋秃蛂educeRight的回調(diào)。
// ContextComposer.tsx import React from 'react'; type IContextComposerProps = { contexts: React.ReactElement[]; }; const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => { return ( <> {contexts.reduceRight((child, parent) => { return React.cloneElement(parent,{},child); }, children)} </> ); }; export default ContextComposer; // App.tsx <ContextComposer contexts={[ <ThemeContext.Provider value={"light"} />, <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }} />, <LoadingContext.Provider value={{ loading, setLoading }} />, ]}> { children } </ContextComposer>
經(jīng)過改造后,我們在傳參時就好像是真的在創(chuàng)建一個組件(當(dāng)然實際上也創(chuàng)建了組件,只是這個組件本身沒有被渲染到虛擬Dom上,實際渲染上去的是被克隆后的副本)。同時,我們剛才關(guān)注的value的靜態(tài)類型檢查問題也得到了解決。
tips: React.cloneElement(parent,{},child)等價于React.cloneElement(parent,{children:child}),你知道為什么嗎?
到此,相信大家對“怎么封裝React Context Composer”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。