context
(上下文)个人理解就是当前环境
首先它是一个属性的有序序列, 并且可以驻留在环境里
有点像全局变量
存在于React 中
react 数据流
单项数据流 ,单纯在React 中
组件内 通过state交流。 父子组件通过props 传递
为了解决 层级传递的繁琐, 于是有了 context ,
Context 旨在解决组件间的数据传递(Super-->Sub 间)
版本 情况
React 16.3 之前 只有 老版context API ,之后老版和新版(createContext)都有。共存。
写法差异
context 提出了一个概念 就是 数据的提供者 和 消费者
这个在新旧API 中 有不同的实现方式
1.老版本的context
Super
- getChildContext 根组件中声明一个函数,返回一个对象,就是context
- childContextTypes 根组件中声明,指定context的结构类型,如不指定,会产生错误
Sub
- contextTypes 子孙组件中声明,指定要接收的context的结构类型,可以只是context的一部分结构。contextTypes 没有定义,context将是一个空对象。
- this.context 在子孙组件中通过此来获取context
- 无状态组件 声明一个contextTypes,然后第二个参数 就是context
2.新版本的context
- 新版本的React context使用了
Provider
和Customer
模式, 更加明确了提供者 和消费者的概念 。和redux-react的模式非常像。 - 使用 createContext() API 来创建一个Context 对象
const Context = createContext(defaultValue)
- 在顶层组件中使用
<Context.Provider value={context}> ... </Context.Provider>
- 在需要使用context的子级中,使用
render props
的方式来使用
<Context.Consumer> { (value)=>{ return ( <> <div>{value}<div> </> ) } } </Context.Consumer>
看呆萌 ---> old + new
生命周期中的使用
old Context
在old Context中,context不止存在于this.context 中, 在如下生命周期中都可以访问到。前提是你声明了contextTypes
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
从React 16开始,componentDidUpdate不再收到prevContext。
new Context
在新的context 中 不提倡从生命周期中获取context ,在生命周期方法中从context访问值是一种相对常见的用例。而不是将上下文添加到每个生命周期方法中。 只需要将它作为一个 props 传递,然后像通常使用 props 一样去使用它。
like this
<Context.Consumer> {theme => <Button {...props} theme={theme} />} </Context.Consumer> class Button extends React.Component { render() { const {theme} = this.props; return ( <button className={theme ? 'dark' : 'light'}> ... </button> ); } }
使用中的问题
old Context
1、如果顶层组件的context改变,并且我们使用setState 来更新context(只有setState重新render,才能调用getChildContext分发context) ,会导致所有子组件(中间组件)重新加载。
一贯的做法是使用 SCU 或者 PureComponent
React.PureComponent
React.PureComponent
类似于React.Component
。它们之间的区别在于React.Component
它没有实现shouldComponentUpdate()
,而是React.PureComponent
通过浅的prop
和state
比较来实现它。
呆萌展示 ---> 改一下oldContext
但是导致新的问题,context 传递不下去。
现有的原生 Context API 存在着一个致命的问题那就是在 Context 值更新后,顶层组件向目标组件 props 透传的过程中 如果中间某个组件的 shouldComponentUpdate 函数返回了 false,因为无法再继续触 发底层组件的 rerender。新的 Context 值将无法到达目标组件。这样的不确定性对于目标组件来说是完全不可控的 也就是说目标组件无法保证自己每一次都可以接收到更新后的 Context 值。
解决办法 :
- 使用redux、mobx 等 状态管理工具 ---> 废话。
- 使用新版Context API
新版中间层组件可以使用SCU 或者PureComponent来优化性能,同时还不妨碍context 的传递。
新版context数据可以关联,是因为都源于同一个context对象
new Context
暂且没找到什么明显的缺点
优点
old Context
- 可中途上车--> 呆萌 中途上车
new Context
- 中间组件SCU 不会阻断context
- 泾渭分明 :多个context之间互不影响
如何安全的使用context?
https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076
1、context只使用一次,就是在初始化的时候,后续不要再更改了
2、如果想改变,使用依赖注入的方式,使用context 在初始化的时候 将依赖注入,并且后期更新可以使用 forceUpdate 来更新,或者将context存于state 中,回调执行setState 来更新组件。(可以将oldContext改成和new Context 一样的优点)
react-redux 等使用此类方法
我们不应该直接在我们的context中存储状态。相反,我们应该使用context作为依赖注入系统。
看呆萌
原理就是 通过将context 的使用限制改为依赖注入而不是 react 本身的状态容器
react 官方原则是 使用context 最好只使用一次,就是初始化的时候
这波操作可以达到的目的是 中间组件可以使用pureComponent 或者 SCU 来避免更新,同时 context 的consumer 还能更新。
上面的如果使用mobx 等 可以直接将 事件发射器 换成 observable 就ok 具体看 pageD/mobx.js
React.PureComponent
React.PureComponent类似于React.Component。它们之间的区别在于React.Component它没有实现shouldComponentUpdate() 需要我们自己来实现,而是React.PureComponent通过浅比较props和stete 来确定是否更新
文章评论