React ?? 新的 Context API

原文地址:Reacts ?? new Context API

作者:kentcdodds

這不再是一個 實驗性的 API,並且它更符合 工程化 的理念,目前它已成為 React 一級棒的 API

?? :大家可以通過 newsletter 獲取我最新的資訊,我一般每兩周通過郵件發送一次,大家可以通過自己的收件箱獲取更多的內容。

React 中的 context API 相信大家都知道吧,可能跟大夥一樣,當看到 React 的官方文檔是這樣時,都不敢直接使用它。

第一條搜索結果顯示的就是 為什麼不建議使用 context,讓大家瞬間產生憂慮,該章節是這麼描述 context 的:

如果你想讓你的應用更加穩定,就別使用 context,因為這是一個實驗性的 API,在未來的 React 版本中可能會發生改變。

?? 注意,這裡的改變包括 中斷終止不再使用 的含義。

那麼,為什麼還要使用 context 呢

你曾經歷過嘗試在一個 層級很深的組件 中獲取 最外層組件state 的痛苦么,這種痛苦叫 prop drilling,可謂讓人接近崩潰的。當遇到這種情形時,你肯定不會喜歡用 props 來傳遞數據,因為如果中間有個組件發生改變,這個代價將是幾何。

實際上,你可以通過使用常規的 JavaScript module 來規避以上的問題,將數據存放在某個 module 中,就可以實現在任何地方 訪問/導入,但這麼做想要 更新 卻很麻煩(你必須實現一個 event 在數據更新時觸發,通知用戶數據發生改變),並且,服務端渲染module 也會有 影響。

因此,像 redux 這樣的負責 狀態管理 的第三方庫進入了大家的視野。它允許你在任何地方從 store 獲取數據,你需要做的只是使用 <Provider /> 包裝一下,然後就可以神奇地在 connected 的組件中輕鬆地獲取想要的數據了。

然而,如果我告訴你 <Provider /> 就是在使用 context 這個 實驗性 API 呢??? 事實上也是這樣的!provider 組件將數據存進 context 中,connect 高階組件從 context 獲取數據,所以,redux 並不允許你的數據可以在任何地方訪問,context 就是這樣。

所以,為什麼還要使用 context 呢?可能是大家已經深深地愛上它了吧!即使你沒有直接使用 context,你的應用程序也會通過引用像 react-redux, MobX-react, react-router, glamorous 這樣的第三方庫間接用到它。

Context 重生啦

現在清楚了,我們是如此地熱愛 context,但官方文檔的警告依然還在:在 React 的未來版本中,可能不再使用它,好消息是,context 要正式跟大家打招呼了,大家極有可能比之前更愛它。

一個月前,React 團隊 從 yarn,rust 和 Ember 的 rfcs 倉庫 受到啟發,建立了一個自己的 rfcs 倉庫。倉庫第一個 PR 來自 Andrew Clark(React 團隊核心成員),PR 標題為 New version of context,其中 Andrew Clark 概述了未來新版本的 context是怎樣的,之後還存在一些有趣的討論,幾天後,Andrew Clark 就向 React 倉庫提了一個 New context API 的 PR

那麼,到底有什麼改變呢?肉眼估計新的 API 與之前的 API 存在百萬級別的差異。這是我做的一個簡單的 示例

const ThemeContext = React.createContext(light)class ThemeProvider extends React.Component { state = {theme: light} render() { return ThemeContext.provide(this.state.theme, this.props.children) }}const ThemeConsumer = ({children}) => ThemeContext.consume(children)class App extends React.Component { render() { <ThemeProvider> <ThemeConsumer>{val => <div>{val}</div>}</ThemeConsumer> </ThemeProvider> }}

你可能注意到示例中使用到一個 render prop,但實際上並沒有任何關於需要使用 render propcontext API,你可以使用 context API 輕鬆實現 高階組件 或其他功能。

新的 context API 主要由以下三部分組成

  • React.createContext 用於傳遞 初始值(可選擇 使用 bitmask 的一個奇妙的選擇性退出函數),返回一個包含 providerconsumer 的對象
  • provide 函數使用 higher,並可以接收任何值
  • consume 函數在 provider 之後任何地方使用,並傳遞一個返回 JSX 的函數(這有點像 render prop 組件,但 consume 不是組件)。

我對這個 API 充滿了期待,React 團隊 也將會移除 context 是實驗性 API 的警告,因為它現在是框架 一級棒的特性。這也意味著大家將不再那麼擔心使用 context 來解決應用中 prop-drilling 的問題了,對 Redux 也將不再那麼依賴,對 React 將更加喜歡。

我最近看到的,大概意思是:

大家不是很願意保持使用提倡的 render 方法,加重了 prop drilling 問題,所以,最終想通過 redux 來緩解

所以,我認為如果我們不過早或武斷地去破壞 render 方法,我們可能就不會那麼痛苦,即便最終我們實在沒有辦法避免,我們也可以通過核心的 React API 來解決。

Context 實踐

我看到了一個關於 context API(或普通的 render prop pattern)的問題很多次,就是如何組合 providersconsumers,當在一個 render 方法中把一堆 render prop 組件放在一起時,就會像這樣 嵌套

那麼,我們可以做點什麼來避免呢?其實,個人覺得沒有那麼糟糕,如果你覺得這樣並不好,那麼可以使用常規的方法來解決它:utility 函數/組件,下面是一個示例:

const ThemeContext = React.createContext(light)class ThemeProvider extends React.Component {/* code */}const ThemeConsumer = ({children}) => ThemeContext.consume(children)const LanguageContext = React.createContext(en)class LanguageProvider extends React.Component {/* code */}const LanguageConsumer = ({children}) => LanguageContext.consume(children)function AppProviders({children}) { return ( <LanguageProvider> <ThemeProvider> {children} </ThemeProvider> </LanguageProvider> )}function ThemeAndLanguageConsumer({children}) { return ( <LanguageConsumer> {language => ( <ThemeConsumer> {theme => children({language, theme})} </ThemeConsumer> )} </LanguageConsumer> )}class App extends React.Component { render() { <AppProviders> <ThemeAndLanguageConsumer> {({theme, language}) => <div>{theme} and {language}</div>} </ThemeAndLanguageConsumer> </AppProviders> }}

這裡的目標是使用常見的案例,結合特殊功能的函數/組件,使案例更加 工程化

除此之外,大家還可以參考 jmeas 的 react-composer。

但需要提及的是,在實踐中,我並不建議大家嵌套渲染 props components,無論什麼時候,都可以選擇創建多個簡單易用的組件,然後組合使用。

總結

正如上面所說的,我對這個 API 充滿了期待。目前暫未發布,但應該會包含在下一個 minor 版本中。不同擔心,之前的 API 會繼續正常工作,直到下一個 major 版本發布,所以,每個人都有時間遷移。還有不要忘了,React 團隊在 Facebook 有超過 50,000React components 需要維護,所以,將來很有可能會發布一個 codemod 去自動更新大多數人的代碼(就像以往一樣)。

我很高興這個 新 API 能夠提供,正如我在 twitter 中提及的。


推薦閱讀:

如何將喜歡的響應式網站變成APP – manifest icon製作教程無標題文章
實現符合 Promise/A+ 規範的Promise
從process.versions了解Node.js的構成
手把手教你用 SVG 符號和 CSS 變數做出彩色圖標

TAG:前端開發 | React |