(原文鏈接:https://css-tricks.com/digging-into-react-context/)...其實并不深入,但不失為一篇好文章。
????????最近你可能想知道關于context的一些消息,想知道它對于你和你的react項目可能意味著什么?context出現之前,在狀態管理變得異常的復雜,超出了setState的能力的時候,你可能不得不使用第三方庫。感謝優秀的React團隊最近作出的更新,現在React本身的context可以幫助我們解決一些狀態管理方面的問題了。
context解決了什么事情?
? ? ?你怎樣將state傳遞給一個在嵌套的組件樹中的一個子組件,可能你知道你可以用Redux去管理狀態,但你沒必要在任何情況下都去使用Redux。有一個可以不用Redux也不用第三方庫的方法,你可以使用props。
? ? ?加入你要實現的功能具有類似于下面的這種結構:
? ? ? 這些狀態在APP組件中,并且在UserProfile和UserDetails組件中也是需要的,你需要利用props將它傳遞到樹的底層,如果需要這個state的組件在樹的第十層,那這樣的工作會讓人感到乏味、疲憊,并且也非常容易出錯。每一個組件都應該像一個黑盒子,其他組件不應該知道它們不需要的狀態。這是一個和上述情況相匹配的一個程序實例。(譯者述:很抱歉這里用截圖的形式展示代碼,由于代碼插入的功能不能支持JS自定義的組件,故采用截圖的形式展現。如若想本地嘗試,可點擊原文,獲取可復制的源碼)
? ? ? 我們使用了props來將state傳遞給另一個組件,User 組件不需要這個state,但是為了將state傳遞到組件樹的底層,它也通過props收到了這個state。
Context來幫忙了
? ? ? 通過React的Context API,你可以將state存儲在應用程序的全局狀態中,并僅在需要的組件中訪問它,而不需要利用props一層層地往組件樹底層傳遞。
首先,我們可以利用React.createContext()來初始化一個新的Context。
? ? ? 這個新的Context由const來聲明,即圖中的UserContext,你可以看到,我們無需安裝任何的第三方的庫,React V16.3.0及以上版本已經有Context API了。Provider組件使得需要context內容的組件(訂閱者)能夠獲取它,換句話說,Provider組件允許消費者訂閱context中的更改。請記住,context就類似于全局應用程序狀態。因此,不是消費者的組件不會獲取到context內容。
? ? ? 如果你在本地嘗試,那你的context 文件應該類似于下圖:
Provider
? ? ? 我們將在我們的父組件中使用Provider,在該組件中我們有state需要傳遞
? ? ? Provider 接受一個value作為props來傳遞給底層的消費者組件,在本文的示例中,我們將user這個狀態傳遞給消費者組件。你可以看到,我們并沒有將它作為props傳遞給User組件。這也意味著我們可以在不需要props的情況下編輯User組件,因為它本身也不需要它們。
消費者(Consumer)
? ? ? 多個消費者組件可以訂閱一個Provider組件,我們的UserProfile需要利用context,所以我們讓它來訂閱、觀察Provider。
? ? ? 我們通過value props的方式注入到Provider中的數據將在函數的context參數中能獲取到,現在我們可以使用這種方法在我們的組件中獲取到用戶的姓名。
? ? ? UserDetail組件現在看起來很像UserProfile,因為它們訂閱的是同一個Provider。
更新state
? ? ? 如果我們允許用戶更改他們的姓名怎么辦,這也是可行的。當通過Provider組件傳遞過來的值改變的時候,consumer(消費者)組件會重新渲染。在消費者組件組件中,我們將有兩個輸入框,分別用于名字與姓氏。在Provider組件中,我們有兩個函數,接收從輸入框中輸入的值,用于更新應用程序的狀態。
? ? ? APP組件源碼見下圖:
? ? ? ?我們將含有state和action的對象傳遞給Provider接受的value props的Provider。這些actions就是當onChange事件發生的時觸發的觸發的函數,事件發生得到的值可以用來更新state,因為我們想要更新的是名字和姓氏,而不需要其他值,基于此,我們利用ES6的擴展操作符,它允許我們更新指定的值。
? ? ? 有了這些變化,我們也需要更新UserProfile組件。
我們使用ES6的解構來提取從Provider中獲取到的值中的state
? ? ? 對于UserDetails組件,既有state又有actions,需要加入兩個輸入框,并且監聽它們的onChange事件,從而去觸發相應的反應函數。
使用默認值
? ? ? 初始化context的時候我們可以傳遞一些默認值。所以我們可以在context中傳遞一些數據(默認值)而不是一個空對象。
? ? ? 要在組件樹種使用這些數據,我們必須從樹中刪除Provider。所以我們的APP組件應該是這樣的:
? ? ? 初始化新的context時,這些數據將被用于consumer(消費者)組件中。
總結
? ? ? 當事情變得越來越復雜,你嘗試著yarn install第三方的狀態管理庫的時候,請暫停幾秒,你可以利用React context。不相信我?也許你會相信Kent C. Dodds.