Skip to content

React ⚛️ 的 Context API #6

Description

@penouc

React ⚛️ 的 Context API


原文链接:https://medium.com/dailyjs/reacts-%EF%B8%8F-new-context-api-70c9fe01596b

这个新的 API 更加符合我们的使用习惯,它不再是「实验性」的功能,现在它是 React 的一级 API 了,并且它还可以使用 Render 属性。

不知道你之前有没有听说过 react 中的 context API? 如果你听说过,那么你是不是跟很多其他人一样在使用这个的时候十分的担忧,因为它的官方文档上是这么写的:

上面的第一个结果就展示了『 为什么不使用 Context 』。不要完全的信任 context API。更多的忧虑,里面有一段说到 :

如果你想让你的应用变得稳定,不要使用 context。因为它是一个实验性的 API 并且在未来有可能会在 React 的正式版本中被废除。

那么为什么要用 React 的 context API 呢

不知道你是否体会过从 state 的顶部取值传递到 react 树的底部的痛苦? 你体会到的这种痛苦被称为为『属性下钻』,传递属性的这个过程特别令人反感。你厌倦这么做,因为在传递属性的过程中的组件毫不关心这份数据,只有最后需要使用这份数据的组件才关心这份数据。然后如果你要移动组件的时候这种痛苦要放大十倍。

你可以通过构建一个常规 JS 模块来避免这种痛苦,把全局的数据放在上面创建的 JS 模块中,这样全局的任何位置都能引用到这个文件了。但是需要更新这份数据的时候就有问题了(你不得不实现一个 event emitter 来通知订阅者当这份数据更新的时候),并且在使用服务端渲染的时候这种单例模式没法处理无法处理单例
所以一些 state 的管理库出现了,比如 redux 。 这些库允许你更加方便的在 react 树中从 store 中取数据。你要实现这样只需要使用 redux 提供的一个根组件 <Provider /> 并且通过组件之间的连接(connected)来保证你的数据能够随时随地可取。
那我必须要告诉你<Provider /> 内部就是使用的这个实验性的 context 特性了!provider 这个组件将数据放入到 context 中,并且通过 connect 将其连接到高阶组件,这个高阶组件会从 context 之外拉取数据。所以其实,redux 并不允许你的数据在每个地方都是可取的,但是 context 可以做到这一点。
所以为什么不使用 context 呢?我能保证你一定会爱上使用这个的!即使你没有直接使用 context 这个特性,你的应用也通过 react-reduxMobX-react, react-router, glamorous  这些库在使用它。

Context 重生

我们爱 context,但是还记得「it is likely to break in future releases of React」这个提醒吗?这次它真的来了,我们马上就可以使用它了。

大约一个月之前,在 YarnRustEmber 团队的鼓励下, React 开发团队创建了一个叫做 RFC 的仓库。这个仓库的第一个 PR 来自 react 核心团队的成员 Andrew Clark 并且它被命名为 『新 Context』

所以新的 context 到底长啥样呢?它比原来的 context 好理解很多倍,下面是一个简单的样例:

Edit Example of React's new context API

还有一个更简单的例子,你都不用打开 codesandbox:

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() {
    return (
      <ThemeProvider>
        <ThemeContext.Consumer>
          {val => <div>{val}</div>}
        </ThemeContext.Consumer>
      </ThemeProvider>
    )
  }
}

你也许注意到例子中,我在使用 prop Consumer 组件渲染(非常好用),但是如果她不是你的菜,你可以使用高阶函数将它包裹起来,或者用别的什么方法来使用它(context API)。

这个新的 context API 主要包含三个方面:

  • React.createContext 是一个方法,初次构建的时候需要传入一个初始值。(有可能还可以传入一个额外的参数)。 这个函数将返回一个包含 ProviderConsumer的对象
  • Provider组件是用在组件树的上面,它支持接受为 value 的 prop (可以是任何值)
  • Consumer 组件使用在 Provider 组件之中的,并且它接受一个 prop 作为 “children” ,并且这个 “children” 必须是一个可以接受参数的函数,同时它必须返回一个 react 元素 (JSX)。

学习这个 API 我真的特别的开心。React 的研发团队将会移除之前的提示,就是之前的那个实验性的提示,因为它现在已经是这个框架的一级特性了。这就表示了人们将会更少的将自己的关注点放到如果实现一个属性下钻的应用了,这个特性可以让更多用户时候可能会考虑不那么早的时候使用 redux,而仅仅只是使用纯的 react 框架来开发更长的时间(或者,使用 Unstated 这个库来解决这个问题)。

我之前发过twitter:

练习使用 Context

一个非常的常见的使用 new Context API 的问题就是如何结合使用 providers 和 consumers。如果你将很多的含有渲染属性的组件写在一个渲染方法里,场面会变得特别难看 … 会很冗余:

所以我们要如何才能避免这个问题呢?如果你在被这个问题困扰,你可以通过和解决普通 JS 问题一样的方式解决这个问题: 将 JSX 组件包装成一个函数类的组件,这里就是示例:

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() {
    return (
      <AppProviders>
        <ThemeAndLanguageConsumer>
          {({theme, language}) => <div>{theme} and {language}</div>}
        </ThemeAndLanguageConsumer>
      </AppProviders>
    )
  }
}

按照以上的操作就能覆盖大多数的场景并且使用一个函数式的组件可以更加的符合开发的思维。就像你是用普通的 Javascript 一样!你意识到这一点了吗,我希望你能够 get 到。

我另外能够展示一个嵌套非常厉害的例子,在使用了一个叫做 react-composer的库之后变得非常好用:

Edit Example of React's new context API

但是我必须说到的是我不太希望能用到嵌套的多个渲染属性的组件,无论你什么时候,你都能够构建一个简单的包含了组合了组件的组件。

总结

如我所说,这个 API 令我非常的证金,它现在还没有发布,但是会在下一个版本中被支持。不用担心,老的 context API 可以一直用到下一个大版本更新,所以其实有时间进行迁移。并且不要忘了 react 团队有 50000 个 react 组件需要更新,所以肯定会有自动更新支持新 API 的方式。

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions