Why React Contexts Are Great and Why We Didn’t Use Them
(還是翻譯)
前言
Web 應(yīng)用程序維護(hù)狀態(tài)以實(shí)現(xiàn)更高級(jí)的用戶交互,當(dāng)我們將電子郵件輸入網(wǎng)站時(shí),我們希望該網(wǎng)站能夠記住這個(gè)電子郵件,而不是在顯示的時(shí)候一遍又一遍地重復(fù)輸入它。
在 React 渲染中記住信息的一種方法是使用狀態(tài),特別是 useContext 鉤子。我查看了 useContext 并開(kāi)始在項(xiàng)目中使用它們,但最終改為了使用組件傳參。這是我在這個(gè)過(guò)程中學(xué)到的。
什么是 React Contexts?
React Contexts 允許讀取和寫(xiě)入狀態(tài)對(duì)象,以任何形式存入 useContext 掛鉤,并通過(guò) provider 組件在任何組件層級(jí)結(jié)構(gòu)中訪問(wèn)。狀態(tài)可以是任何變量和函數(shù)的集合。
provider 是一個(gè) React 組件,它設(shè)置初始狀態(tài)并允許所有后代訪問(wèn)改狀態(tài)。這種簡(jiǎn)單性使得 React Contexts 變得很靈活。
它還允許在 Web 應(yīng)用程序中使用多個(gè)不同的Contexts,具體取決于應(yīng)用程序中呈現(xiàn)的組件。你可以使用狀態(tài)本身的一部分函數(shù)來(lái)修改 React Contexts 中的變量。
使用 React Contexts 的好處包括易于導(dǎo)入,并且能夠?qū)?useContext 掛鉤添加到任何組件而不需要修改傳參。
我們?cè)谀陌l(fā)現(xiàn)的問(wèn)題?
我們了解了useContext,并開(kāi)始在我們的項(xiàng)目中使用它,以存儲(chǔ)用戶工作流中所有步驟的信息。我們添加了工作流所需的信息,并很快看到Contexts狀態(tài)隨著函數(shù)和變量增多而擴(kuò)展。這本身并不是一個(gè)大問(wèn)題。狀態(tài)類型都在一個(gè)文件中,保持可讀。
問(wèn)題是,修改當(dāng)前工作流變量不需要任何權(quán)限。當(dāng)組件導(dǎo)入useContext時(shí),它會(huì)獲得完全的讀寫(xiě)權(quán)限,工作流難以判斷是否是意外操作導(dǎo)致變量修改的操作。在開(kāi)發(fā)前的四周內(nèi),該狀態(tài)發(fā)生了有趣的行為,我們可以單機(jī)一個(gè)按鈕,這個(gè)更改將會(huì)通過(guò)多個(gè)步驟和組件進(jìn)行傳播。然而,即使小團(tuán)隊(duì)中每個(gè)人對(duì)組件庫(kù)都很熟悉,這樣也會(huì)使代碼變得很難維護(hù)。
我們做了什么?
我們決定轉(zhuǎn)移到一個(gè)狀態(tài)對(duì)象,該對(duì)象從一個(gè)組件傳遞到另一個(gè)組件。好處是我們可以在每一層進(jìn)行檢查,以確保只給暴露給組件所需要的狀態(tài)和功能。這有利于我們縮小每個(gè)組件的職責(zé),提高代碼可讀性。
我們發(fā)現(xiàn)React Contexts支持簡(jiǎn)單數(shù)據(jù)共享。然而這個(gè)工具需要有規(guī)律地來(lái)有效控制Contexts中的內(nèi)容。我們團(tuán)隊(duì)是否因?yàn)闆](méi)有使用React Contexts而失去了機(jī)會(huì)?這是我們理解上的問(wèn)題還是工具本身的問(wèn)題?請(qǐng)?jiān)谠u(píng)論區(qū)告訴我,并且我們始終嘗試為每個(gè)項(xiàng)目找到正確的模式。
留言
評(píng)論1
謝謝你的分享,使用全局Contexts聽(tīng)起來(lái)更像是一種反面模式(指的是在實(shí)踐中明顯出現(xiàn)但又低效或是有待優(yōu)化的設(shè)計(jì)模式)。我發(fā)現(xiàn)有價(jià)值的是使用多個(gè)Contexts,每個(gè)Contexts都專注于某個(gè)域。這樣組件將只獲得所需要的東西。
評(píng)論2
在我看來(lái),如果不使用 setter 函數(shù),你反對(duì)的觀點(diǎn)就站不住腳。
比較好的方案是只允許父組件去修改狀態(tài)。
如果你只是希望某些不同的組件能設(shè)置狀態(tài), 你也可以將Contexts拆成狀態(tài)和 setter Contexts。只要 setter 引用穩(wěn)定,只更改狀態(tài)的組件只需要訪問(wèn)后者,也不會(huì)由于狀態(tài)更新而重新渲染。這樣你可以通過(guò)查找 setter 而鎖定到更改狀態(tài)的子級(jí)。
這可能在某些項(xiàng)目中不起作用,但我覺(jué)得你重新引入了Contexts避免的東西:prop-drilling(在 React 中經(jīng)常會(huì)碰到父組件一層一層地傳參到子組件,這種行為叫做prop-drilling,drilling是下鉆的意思)。
評(píng)論3
從我對(duì)你項(xiàng)目的有限理解來(lái)看,我不太確定你為什么選擇在項(xiàng)目中用Contexts API。聽(tīng)起來(lái)你構(gòu)建了很多用戶表單,但沒(méi)有人主張?jiān)诒镜亟M件或工作流中使用Contexts。你這樣說(shuō)是因?yàn)檫@在代碼庫(kù)中是不長(zhǎng)久的。Contexts 不是用來(lái)替代狀態(tài)的,它也不是用來(lái)替代像 redux 這種狀態(tài)管理工具的,而是用來(lái)替代深層組件樹(shù),或者不相干的組件(無(wú)共同父級(jí)),以便輕松訪問(wèn)共同的狀態(tài)值。
評(píng)論4
這根本不是Contexts的使用方式,官方文檔會(huì)在你使用之前就告訴你這一點(diǎn)。不太清楚博客里是如何保證這一點(diǎn)的,因?yàn)椤拔覀儑L試了一個(gè)工具,卻不知道的他是如何工作的,當(dāng)我們不知道它是如何工作時(shí),我們就放棄了它”。
評(píng)論5
Immense swag here ngl (不知道這個(gè)咋翻譯)
評(píng)論6
所以你用 useContext 取代了 Props下鉆……
我認(rèn)為 Contexts 是行的,但不是直接以原生的方式使用,因?yàn)樗鼤?huì)導(dǎo)致沒(méi)必要的re-render,我們需要以冗余的方式包裝它,這樣我們就可以使用 memoization,因?yàn)?useContext 將在組件內(nèi)使用,它不關(guān)心 useMemo
評(píng)論7
實(shí)際上,我找到了一種修改useReducer和createContext的方法,以便只為特定組件提供我想要讀取的屬性。
我注意到,一旦在具有全局狀態(tài)的組件上使用Context,它將在每次更改狀態(tài)中的一個(gè)項(xiàng)時(shí)重新讀取。這是因?yàn)楫?dāng)你創(chuàng)建一個(gè)上下文時(shí),它有一個(gè)鍵“Provider”,和鍵“Consumer”,你需要?jiǎng)h除Consumer和Provider,并為它們放置自己的代碼,以處理注冊(cè)自己的監(jiān)聽(tīng)器和何時(shí)更新,而不是React自己做這項(xiàng)工作。當(dāng)reducer更新?tīng)顟B(tài)時(shí),需要更改useReducer。您可以通過(guò)在回調(diào)認(rèn)為必要時(shí)強(qiáng)制更新來(lái)實(shí)現(xiàn)這一點(diǎn)
個(gè)人總結(jié)
以為是技術(shù)分享,但實(shí)際上是針對(duì)React的Contexts使用上的討論,這個(gè)也有使用過(guò),但確實(shí)【評(píng)論6】的說(shuō)法是現(xiàn)在項(xiàng)目插件中比較主流的,如開(kāi)發(fā)中使用的useModel,是umi框架中的一個(gè)Hooks。(諸如其他的插件,我也還沒(méi)去了解過(guò),有緣再分享這種插件。)