react新API——context上下文

React’s ⚛️ new Context API

这是一种更人性化的方式,不再是“实验性功能”,并且现在它是一级API。并且使用了 render prop!

你之前听说过 react 的 context API 吗?如果你听说过,你是否和其他用户一样犹豫是否直接使用它,因为你会在官方文档看到如下内容:

搜索的第一个内容就是“为什么不要去使用 context”。这不能激发使用 context API 的信心。为了考虑风险,部分提到:

如果想让你的应用更稳定,那不要使用 context。这是一个实验性的 API,在未来发布的 react 版本中可能会被移除。

所以,为什么会想要使用 context 呢?

你是否经历过将 state 从 react 树的根组件一直传递至叶节点的痛苦呢?这种痛苦被称为“prop drilling”,并且非常烦人。你必须让不关心该状态的组件传递 props 给关心该数据的子组件。特别是当你移动组件时,这种痛苦感更强烈了。

你当然可以使用标准 javascript 模块来避免这个问题。只需要将数据放到一个单例模块中,它可以在任何地方被访问或导入。但你可能在更新数据的时候遇到一些问题(你必须实现一个事件广播让订阅者知道何时更新了数据),并且服务端渲染也会对单例造成困扰

所以,像 redux 这类的状态管理库就应运而生了。它允许你在 react 树的任何地方获取数据。你所要做的事只是使用一个叫 的东西,然后你的组件就可以通过 connected 神奇般地访问你的 store 数据。

如果我告诉你 使用了 context这个实验性功能会怎么样?😱 这是真的!provider 组件将数据放在 context ,然后 connect 高阶组件从 context 读取数据。所以实际上, redux 并没有实现在任何地方访问数据… 是 context 做了这件事!

那么,现在知道为什么要使用 context 了吗?好吧。你可能已经知道并爱上它了!就算你没有直接使用 context, 你也可能通过 react-redux, MobX-react, react-router, glamorous 等间接地使用了它!

Context 重生

尽管我们现在很喜欢 context, 但还记得那句“它很可能在将来的 react 版本中被移除”这句话吗?来吧,你会爱上它的!

一个多月以前, react 团队受到 yarn, Rust, Ember 的启发创建了 RFCs 项目。这个项目的第一个 PR 来自 Andrew Clark(react 核心团队成员)并称之为“新版 context”。在这个提交中,Andrew 列出了新版 context 要实现的功能。这里面有一些有趣的讨论。几天后,Andrew 开了一个新 PR “新 context API”。

所以,它看起来像什么?它比旧的 context API 要直观一百万倍。这是我想到的最简单有用的例子:

这是最简单的版本:

const ThemeContext = React.createContext('light')
class ThemeProvider extends React.Component {
  state = {theme: 'light'}
  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        {this.props.children}
      ThemeContext.Provider>
    )
  }
}
class App extends React.Component {
  render() {
    <ThemeProvider>
      <ThemeContext.Consumer>
        {val => <div>{val}div>}
      ThemeContext.Consumer>
    ThemeProvider>
  }
}

你可能注意到我在消费组件中使用了 render props(最佳实践!),但如果这不是你的菜,你也可以很容易地使用高阶组件或用了 context API 的其他方法去实现(这就是为什么它是最佳的)。

新的 context API 由以下几部分组件:

  • React.createContext 传递初始值(可选的使用位掩码功能)。这返回了一个包含 ProviderConsumer 的对象。
  • Provider 在组件树中使用频率更高,并且接受一个 value 属性(可以是任何值)。
  • Consumer 组件可以在 Provider 组件树下的任何地方使用,并接受一个“children”属性, children 必须是一个接受 value 值并返回 react 元素(JSX)的函数。

看到这个API 我异常地兴奋。react 团队将会移除 context 是一个实验性功能的警告,因为它现在是这个框架的一等功能。这意味着开发者有希望使用 context 简单地解决 prop-drilling 问题,而不必借助 redux 这类工具来解决这个痛点,也能更好更自由地使用简单的 react 。(又或者, James Kyle未发布的的解决方案是我们所期待的)

Context 实践

关于这个新的 context API,我遇到的比较多的问题是(或者其他的 props render模式),如何组合 providers 和 consumers 组件。当你将一堆的 render prop 组件放在一起时,就会变成 嵌套:

那我们如何避免这个问题?如果这让你很烦恼,那你可以像解决常规的 javascript 问题一样:使用实用函数/组件。这有个例子:

const ThemeContext = React.createContext('light')
class ThemeProvider extends React.Component {/* code */}
const ThemeConsumer = ThemeContext.Consumer
const LanguageContext = React.createContext('en')
class LanguageProvider extends React.Component {/* code */}
const LanguageConsumer = LanguageContext.Consumer
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>
  }
}

上面举例了一个常见的案例,并使用特殊的函数或组件去让这些案例更人性化。就像你平时处理问题一样,举对例子了吗?我希望有用 😅

总结

就像我上面说的,我对这个 API 非常感兴趣。它现在还未发布,但将发布在下一个版本的 react。不要担心,旧的 context API 将支持到下一个主版本号发布。所以每个人都会有足够的时间迁移。不要忘了,react 团队有超过 50,000 react 组件,很可能会发布一个 codemod 来自动升级大家的代码。

0%