(譯)React ⚛️ 新的 Context API

創宇前端發表於2018-02-03

原文地址:React's ⚛️ new Context API

作者:kentcdodds

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

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

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

(譯)React ⚛️  新的 Context API

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

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

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

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

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

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

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

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

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

Context 重生啦

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

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

那麼,到底有什麼改變呢?肉眼估計新的 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 元件放在一起時,就會像這樣 巢狀

(譯)React ⚛️  新的 Context API

那麼,我們可以做點什麼來避免呢?其實,個人覺得沒有那麼糟糕,如果你覺得這樣並不好,那麼可以使用常規的方法來解決它: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>
  }
}
複製程式碼

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

除此之外,大家還可以參考 jmeasreact-composer

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

總結

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

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

(譯)React ⚛️  新的 Context API


關注微信公眾號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!

(譯)React ⚛️  新的 Context API

相關文章