原文地址:https://github.com/rccoder/blog/issues/30
妈妈,我再也不会滥用 Redux 了
前言
context 是各种前后端框架中经常会用到的一个概念,著名 Node 框架 Koa
更是把 context 玩的尽兴。React 在很早之前就有 context 的概念,虽然是一个实验性质的 API,但 react-redux、react -router 等框架类库却把它玩了个够。
React 中爷爷和孙子,甚至是重孙之间传递值或者事件一直是个比较麻烦的事情,随着 Redux 等状态管理类库的出现,大家纷纷开始用这种框架去解决这种隔代传信息的问题,并且在或大或小的项目中都开始使用。
一回喜,二回忧,在前端视资源体积为金子的情况下 “滥用 Redux” 的情况越来越多。
React 新版本中的 context 终于要转正了,并且经过了重新的思考与沉淀,与之前 context 在设计哲学上不一样,但解决的却是同一个问题。这样,或许数据流真的不是很多的项目或许可以真的摆脱一下 Redux 等去试试自带的 Context 了。
本文将介绍新老 Context 的一些用法和自己的一些思考。
子孙传值问题
首先看一下最简单的 爷爷给孙子 传值的问题:
class Children extends React.Component {
render() {
return (
<div>{ this.props.text }div>
)
}
}
class Parent extends React.Component {
render() {
return (
)
}
}
class GrandParent extends React.Component {
render() {
return (
)
}
}
复制代码
这几行代码中都是手动的让 text 一辈一辈 的往下传,如果是传给重孙的话还需要继续手动往下传。
利用 Context 就能让这种信息自动的传递,不再让 中间辈 去担任消息传递人,做一些没太大意义的事。
老 Context
const PropTypes = require('prop-types');
class Children extends React.Component {
static contextTypes = {
text: PropTypes.string
}
render() {
return (
<div>{ this.context.text }div>
)
}
}
class Parent extends React.Component {
render() {
return (
<Children/>
)
}
}
class GrandParent extends React.Component {
static childContextTypes = {
text: PropTypes.string,
}
getChildContext() {
return {
text: 'Hi, my baby'
}
}
render() {
return (
<Parent text="Hi, my baby" />
)
}
}
复制代码
在 GrandParent
上通过 getChildContext
给 context 对象添加了 text
的属性,这个属性可以在 GrandParent
的任何一个子孙(子组件)中访问。
同时,为了方便在各种生命周期中使用 context,部分生命周期都给 context 留了接口,具体可以参考 Referencing Context in Lifecycle Methods。
新 Context
老式的 context 和依靠中间辈一层层去传递数据相比确实是酷酷的,但总感觉不 React。或许这也是为什么之前一直不建议使用的原因吧。
同时还有一个比较?的 context 更新问题,更多可以参考 How to safely use React context。
在 React 16.3 中 新版本的 Context 出炉了,感兴趣的可以参考 rfc提案。
新版 context 之下上面的代码这样写:
const AppContext = React.createContext();
class Children extends React.Component {
render() {
return (
{
context => {
return (
{context.text}
)
}
}
)
}
}
class Parent extends React.Component {
render() {
return (
)
}
}
class GrandParent extends React.Component {
render() {
return (
)
}
}
复制代码
新版 Context 用 createContext
去初始化一个 context,返回的对象中有 provider
和 consumer
方法。provider
是以外层容器的方式去包裹住 context 要作用的最外层组件(使用过 Redux 的同学对这点应该有种似曾相似的感觉),然后需要使用到 context 的时候需要用 consumer
去包裹一下。需要注意的是 consumer
包裹的里面的写法,不是普通的组件。
虽然 context 可以是一个 Object,但还是避免不了业务逻辑中会出现多个 context 的问题,consumer
与 provider
一一对应的模式会造成花式嵌套地狱,可以使用伟大社区产生的 react-context-composer 对 context 进行 composer,源码也非常简单易懂。
Context 会让 Redux 消失吗?
不会,解决的终极问题不完全一样!
Redux 解决的是大型软件架构中数据流传输的问题;context 解决的是子孙之间方便数据交互的问题。有一定的相似性,但不属于同等性质。
延伸阅读
- context - react docs
- How to safely use React context
- rfcs pr: 0002-new-version-of-context.md
- rfcs: 0002-new-version-of-context.md
- React's new Context API - kentcdodds
- 知乎:如何解读 react 16.3 引入的新 context api?
题外
文章内容第一时间更新地址为:https://github.com/rccoder/blog/issues/30。为保证看到的是最新的,交流 & 阅读优先点击 这里。