原文地址:React’s ⚛️ new Context API
作者:kentcdodds
這不再是一個 實驗性的 API
,並且它更符合 工程化
的理念,目前它已成為 React 一級棒的 API
。
⚠️ :大家可以通過 newsletter 獲取我最新的資訊,我一般每兩週通過郵件傳送一次,大家可以通過自己的收件箱獲取更多的內容。
React
中的 context API
相信大家都知道吧,可能跟大夥一樣,當看到 React
的官方文件是這樣時,都不敢直接使用它。
第一條搜尋結果顯示的就是 為什麼不建議使用 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-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 prop
的context API
,你可以使用context API
輕鬆實現高階元件
或其他功能。
新的 context API
主要由以下三部分組成
React.createContext
用於傳遞初始值
(可選擇 使用 bitmask 的一個奇妙的選擇性退出函式),返回一個包含provider
和consumer
的物件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
)的問題很多次,就是如何組合 providers
和 consumers
,當在一個 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,000
個 React components
需要維護,所以,將來很有可能會釋出一個 codemod
去自動更新大多數人的程式碼(就像以往一樣)。
我很高興這個 新 API
能夠提供,正如我在 twitter 中提及的。
關注微信公眾號:創宇前端(KnownsecFED),碼上獲取更多優質乾貨!