原文地址:React's ⚛️ 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 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 中提及的。