随着 React 16.3.0 的发布,context api 也有了很大的更新。我已经从旧版的 api 更新到了新版。这里就分享一下我(作者)的心得体会。

回顾

下面是一个展示如何使用旧版 api 的例子:

  1. function Usage(props) {
  2. return (
  3. <Toggle onToggle={props.onToggle}>
  4. <Toggle.On>The button is on</Toggle.On>
  5. <Toggle.Off>The button is off</Toggle.Off>
  6. <div>
  7. <Toggle.Button />
  8. </div>
  9. </Toggle>
  10. )
  11. }

上面的代码会返回一个复合组件Toggle。这个组件可以让子组件共享隐式的状态。在某些简单的情况下可以用React.Children.map来处理。但是,这个例子需要使用 context api 来达到在 React 的某个组件树的任意节点分享 state 的目的。

旧的 Context Api

这是一个旧版 context api 的应用例子:

  1. const TOGGLE_CONTEXT = "__toggle__";
  2. // Toggle on
  3. function ToggleOn({ children }, context) {
  4. const { on } = context[TOGGLE_CONTEXT];
  5. return on ? children : null;
  6. }
  7. ToggleOn.contextTypes = {
  8. [TOGGLE_CONTEXT]: PropTypes.object.isRequired
  9. };
  10. // Toggle off
  11. function ToggleOff({ children }, context) {
  12. const { on } = context[TOGGLE_CONTEXT];
  13. return on ? null : children;
  14. }
  15. ToggleOff.contextTypes = {
  16. [TOGGLE_CONTEXT]: PropTypes.object.isRequired
  17. };
  18. // Toggle button
  19. function ToggleButton(props, context) {
  20. const { on, toggle } = context[TOGGLE_CONTEXT];
  21. return <Switch on={on} onClick={toggle} {...props} />;
  22. }
  23. ToggleButton.contextTypes = {
  24. [TOGGLE_CONTEXT]: PropTypes.object.isRequired
  25. };
  26. // Toggle
  27. class Toggle extends React.Component {
  28. static On = ToggleOn;
  29. static Off = ToggleOff;
  30. static Button = ToggleButton;
  31. static defaultProps = { onToggle: () => {} };
  32. static childContextTypes = {
  33. [TOGGLE_CONTEXT]: PropTypes.object.isRequired
  34. };
  35. state = { on: false };
  36. toggle = () =>
  37. this.setState(
  38. ({ on }) => ({ on: !on }),
  39. () => this.props.onToggle(this.state.on)
  40. );
  41. getChildContext() {
  42. return {
  43. [TOGGLE_CONTEXT]: {
  44. on: this.state.on,
  45. toggle: this.toggle,
  46. }
  47. };
  48. }
  49. render(){
  50. return <div>{this.props.children</div>
  51. }
  52. }

在就的 API 里,你必须用一个字符串指定要分享的 state。然后通过getChildContext方法返回实际的 context。在父组件里通过childContextTypes来指定发出的 context 的类型,在子组件里用contextTypes来指定接收的 context 的类型。我(作者, 下文同)从来不喜欢这样不直接的方式, 平时也尽量不这么做。另外,还要使用静态属性,才能保证 context 值传入子组件。 我也不喜欢这样。

另一个问题是如果shouldComponentUpdate方法返回 false 的话,context 的值是不会变的。当然这也有变通的方法,具体可以参考这个repo

新的 Context API

新的 context api 没有这些问题,这也是我为什么这么激动的原因。上面的例子可以更新为:

  1. const ToggleContext = React.createContext({
  2. on:false,
  3. toggle: () => {},
  4. });
  5. class Toggle extends React.Component {
  6. static On = ({children}) => (
  7. <ToggleContext.Consumer>
  8. {({on})=>(on ? children: null)}
  9. </ToggleContet.Consumer>
  10. )
  11. static Off = ({children}) => (
  12. <ToggleContext.Consumer>
  13. {({on}) => (on ? null : children)}
  14. </ToggleContext.Consumer>
  15. );
  16. static Button = props => (
  17. <ToggleContext.Consumer>
  18. {({on, toggle}) => (
  19. <Switch on={on} toggle={toggle} {...props} />
  20. )}
  21. </ToggleContext.Consumer>
  22. )
  23. toggle = () => this.setState(
  24. ({on}) => ({on: !on}),
  25. () => this.props.onToggle(this.state.on)
  26. );
  27. state = {on: false, toggle: this.toggle};
  28. render() {
  29. return (
  30. <ToggleContext.Provider value={}>
  31. {this.props.children}
  32. </ToggleContext.Provider>
  33. );
  34. }
  35. }

旧的API的问题都没有了。现在不仅没有了不直接的字符串,还有了明显的组件:ProviderConsumer分别提供和消费context。

每一个子组件都需要用consumer子组件,就想在就api里需要有静态属性一样)。但是,两个api的这个问题都可以通过基于render props的高阶组件来解决。非常简单!

另一个通过shouldComponentUpdate返回false来更新的问题也解决了。新的context api会自动处理这个问题。

最后一个非常好的改变是子组件都使用了render props模式。这样在新的context api里也可以对外界暴露非常优雅的接口。

新api的问题

这就是在新的api里通过Provider的value属性给子组件消费的值,只有在你想要子组件重绘的时候才会改变。

这也就是说在render方法里使用`value={{on: this.state.on, toggle: this.toggle}}是不被推荐的。

这是因为没次render都会传入一个新的对象,即使state本身没有改变。
因为是一个新的对象,那么所有的子组件也都会重绘。

这个影响在实际使用的时候会很大。一般来说最好是传入一个只有在state改变时才改变的值。也就是为什么说value={this.state}

如果你不想传入整个state给消费者,那么你可以使用这个Ryan Florence的这个小技巧

只不过这也还是会有一个小问题,我需要把toggle方法存入state里。这也显得很奇怪,不过这只是一个小瑕疵,不影响大局。

总结

新的context api绝对是React团队带来的一个非常好的改变。希望你也能和我一样的喜欢。

另外:如果你还不能用16.3.0这个版本,那么你可以添加一个polyfill来使用新的api。这个polyfill是create-react-context

from:https://blog.kentcdodds.com/migrating-to-reacts-new-context-api-b15dc7a31ea0

[译]迁移到新的 React Context Api的更多相关文章

  1. [React] Use the new React Context API

    The React documentation has been warning us for a long time now that context shouldn't be used and t ...

  2. React Context API

    使用React 开发程序的时候,组件中的数据共享是通过数据提升,变成父组件中的属性,然后再把属性向下传递给子组件来实现的.但当程序越来越复杂,需要共享的数据也越来越多,最后可能就把共享数据直接提升到最 ...

  3. 10分钟学会React Context API

    Create-react-app来学习这个功能: 注意下面代码红色的即可,非常简单. 在小项目里Context API完全可以替换掉react-redux. 修改app.js import React ...

  4. React 16.3来了:带着全新的Context API

    文章概览 React在版本16.3-alpha里引入了新的Context API,社区一片期待之声.我们先通过简单的例子,看下新的Context API长啥样,然后再简单探讨下新的API的意义. 文中 ...

  5. React 新 Context API 在前端状态管理的实践

    本文转载至:今日头条技术博客 众所周知,React的单向数据流模式导致状态只能一级一级的由父组件传递到子组件,在大中型应用中较为繁琐不好管理,通常我们需要使用Redux来帮助我们进行管理,然而随着Re ...

  6. React 全新的 Context API

    Context API 可以说是 React 中最有趣的一个特性了.一方面很多流行的框架(例如react-redux.mobx-react.react-router等)都在使用它:另一方面官方文档中却 ...

  7. 手写一个React-Redux,玩转React的Context API

    上一篇文章我们手写了一个Redux,但是单纯的Redux只是一个状态机,是没有UI呈现的,所以一般我们使用的时候都会配合一个UI库,比如在React中使用Redux就会用到React-Redux这个库 ...

  8. 简单整理React的Context API

    之前做项目时经常会遇到某个组件需要传递方法或者数据到其内部的某个子组件,中间跨越了甚至三四层组件,必须层层传递,一不小心哪层组件忘记传递下去了就不行.然而我们的项目其实并没有那么复杂,所以也没有使用r ...

  9. React Hooks & Context API

    React Hooks & Context API responsive website https://reactjs.org/docs/hooks-reference.html https ...

随机推荐

  1. 第一周pta作业1总结

    查找整数 本题要求从输入的N个整数中查找给定的X.如果找到,输出X的位置(从0开始数):如果没有找到,输出"Not Found". 输入格式: 输入在第一行中给出两个正整数N(≤2 ...

  2. 认识一下margin

    标签(空格分隔): margin <!DOCTYPE html> <html lang="en"> <head> <meta charse ...

  3. word文件转html字符串(包涵格式和图片)

    新项目客户有需求,用word编辑新闻,上传到服务器并显示到富文本编辑器,编辑后保存为html格式的文本.实现如下: 首先引用 Microsoft.Office.Interop.Word.dll(需要安 ...

  4. Windows防火墙开启ping,禁ping的配置

    当我通过本机Ping另一台在同一局域网内(即在同一网段)的计算机时,发现,如果防火墙开启的话,无论如何也ping不通.一旦关闭防火墙就可以ping通了.这是为什么呢?究竟该怎么设置呢? 原因是这样的, ...

  5. django RESTful设计方法

    1. 域名 应该尽量将API部署在专用域名之下. https://api.example.com 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下. https://example.org ...

  6. css样式之input输入框默认样式

    帮朋友写个简单的课程设计,后面会贴出来,项目刚开始就遇到一个坑(给input输入框设定样式,但是,点击后会出现蓝色边框),之前写其他的项目时也遇到过,百度一下资料解决了,现在又碰到了,写一下,留着备用 ...

  7. appium三种等待时间

    1.强制等待(固定等待) 2.隐式等待 是appium中webdriver中自带的休眠方法,设置的是全局等待时间(在全局等待时间内之间的响应操作都会立即结束等待,然后进行操作) 3.显式等待

  8. CRM--自定义stark

    一:启动 解释django的启动 完成以上两步,在app01下创建stark.py文件,就会被加载 (admin.py就是这样启动的) 二:注册 三:设计urls 四:增删改查

  9. JMeter 通过JSON Extractor 插件来提取响应结果

    接口响应结果,通常为HTML.JSON格式的数据,对于HTML的响应结果的提取,可以通过正则表达式,也可以通过XPath 来提取. 对于JSON格式的数据,可以通过正则表达式.JSON Extract ...

  10. 多线程同步synchornized、volatile、Atomic、CountDownLatch示例

    synchronized关键字 锁对象.synchronized(this)和synchronized方法都是锁当前对象. import java.util.concurrent.TimeUnit; ...