React ⚛️ 新的 Context API

原文地址: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-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 组件放在一起时,就会像这样 嵌套

那么,我们可以做点什么来避免呢?其实,个人觉得没有那么糟糕,如果你觉得这样并不好,那么可以使用常规的方法来解决它: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 中提及的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容