这一节我们来介绍一个你可能永远用不上的 React.js 特性 —— context。但是了解它对于了解接下来要讲解的 React-redux 很有好处,所以大家可以简单了解一下它的概念和作用。

在过去很长一段时间里面,React.js 的 context 一直被视为一个不稳定的、危险的、可能会被去掉的特性而不被官网文档所记载。但是全世界的第三方库都在用使用这个特性,直到了 React.js 的 v0.14.1 版本,context 才被官方文档所记录。

除非你觉得自己的 React.js 水平已经比较炉火纯青了,否则你永远不要使用 context。就像你学 JavaScript 的时候,总是会被提醒不要用全局变量一样,React.js 的 context 其实像就是组件树上某颗子树的全局变量。

想象一下我们有这么一棵组件树:

假设现在这个组件树代表的应用是用户可以自主换主题色的,每个子组件会根据主题色的不同调整自己的字体颜色或者背景颜色。“主题色”这个玩意是所有组件共享的状态,根据我们在 前端应用状态管理 —— 状态提升 中所提到的,需要把这个状态提升到根节点的 Index 上,然后把这个状态通过 props 一层层传递下去:

假设原来主题色是绿色,那么 Index 上保存的就是 this.state = { themeColor: 'green' }。如果要改变主题色,可以直接通过 this.setState({ themeColor: 'red' })来进行。这样整颗组件树就会重新渲染,子组件也就可以根据重新传进来的 props.themeColor 来调整自己的颜色。

但这里的问题也是非常明显的,我们需要把 themeColor 这个状态一层层手动地从组件树顶层往下传,每层都需要写 props.themeColor。如果我们的组件树很层次很深的话,这样维护起来简直是灾难。

如果这颗组件树能够全局共享这个状态就好了,我们要的时候就去取这个状态,不用手动地传:

就像这样,Index 把 state.themeColor 放到某个地方,这个地方是每个 Index 的子组件都可以访问到的。当某个子组件需要的时候就直接去那个地方拿就好了,而不需要一层层地通过 props 来获取。不管组件树的层次有多深,任何一个组件都可以直接到这个公共的地方提取 themeColor 状态。

React.js 的 context 就是这么一个东西,某个组件只要往自己的 context 里面放了某些状态,这个组件之下的所有子组件都直接访问这个状态而不需要通过中间组件的传递。一个组件的 context 只有它的子组件能够访问,它的父组件是不能访问到的,你可以理解每个组件的 context 就是瀑布的源头,只能往下流不能往上飞。

我们看看 React.js 的 context 代码怎么写,我们先把整体的组件树搭建起来,这里不涉及到 context 相关的内容:

class Index extends Component {
render () {
return (
<div>
<Header />
<Main />
</div>
)
}
} class Header extends Component {
render () {
return (
<div>
<h2>This is header</h2>
<Title />
</div>
)
}
} class Main extends Component {
render () {
return (
<div>
<h2>This is main</h2>
<Content />
</div>
)
}
} class Title extends Component {
render () {
return (
<h1>React.js 小书标题</h1>
)
}
} class Content extends Component {
render () {
return (
<div>
<h2>React.js 小书内容</h2>
</div>
)
}
} ReactDOM.render(
<Index />,
document.getElementById('root')
)

代码很长但是很简单,这里就不解释了。

现在我们修改 Index,让它往自己的 context 里面放一个 themeColor

class Index extends Component {
static childContextTypes = {
themeColor: PropTypes.string
} constructor () {
super()
this.state = { themeColor: 'red' }
} getChildContext () {
return { themeColor: this.state.themeColor }
} render () {
return (
<div>
<Header />
<Main />
</div>
)
}
}

构造函数里面的内容其实就很好理解,就是往 state 里面初始化一个 themeColor 状态。getChildContext 这个方法就是设置 context 的过程,它返回的对象就是 context(也就是上图中处于中间的方块),所有的子组件都可以访问到这个对象。我们用 this.state.themeColor 来设置了 context 里面的 themeColor

还有一个看起来很可怕的 childContextTypes,它的作用其实 propsType 验证组件 props 参数的作用类似。不过它是验证 getChildContext 返回的对象。为什么要验证 context,因为 context 是一个危险的特性,按照 React.js 团队的想法就是,把危险的事情搞复杂一些,提高使用门槛人们就不会去用了。如果你要给组件设置 context,那么 childContextTypes 是必写的。

现在我们已经完成了 Index 往 context 里面放置状态的工作了,接下来我们要看看子组件怎么获取这个状态,修改 Index 的孙子组件 Title

class Title extends Component {
static contextTypes = {
themeColor: PropTypes.string
} render () {
return (
<h1 style={{ color: this.context.themeColor }}>React.js 小书标题</h1>
)
}
}

子组件要获取 context 里面的内容的话,就必须写 contextTypes 来声明和验证你需要获取的状态的类型,它也是必写的,如果你不写就无法获取 context 里面的状态。Title 想获取 themeColor,它是一个字符串,我们就在 contextTypes 里面进行声明。

声明以后我们就可以通过 this.context.themeColor 获取到在 Index 放置的值为 red 的 themeColor,然后设置 h1 的样式,所以你会看到页面上的字体是红色的:

如果我们要改颜色,只需要在 Index 里面 setState 就可以了,子组件会重新渲染,渲染的时候会重新取 context 的内容,例如我们给 Index 调整一下颜色:

...
componentWillMount () {
this.setState({ themeColor: 'green' })
}
...

那么 Title 里面的字体就会显示绿色。我们可以如法炮制孙子组件 Content,或者任意的 Index 下面的子组件。让它们可以不经过中间 props 的传递获就可以获取到由 Index 设定的 context 内容。

总结

一个组件可以通过 getChildContext 方法返回一个对象,这个对象就是子树的 context,提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证。

如果一个组件设置了 context,那么它的子组件都可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过 contextTypes 来声明你想要的 context 里面的哪些状态,然后可以通过 this.context 访问到那些状态。

context 打破了组件和组件之间通过 props 传递数据的规范,极大地增强了组件之间的耦合性。而且,就如全局变量一样,context 里面的数据能被随意接触就能被随意修改,每个组件都能够改 context 里面的内容会导致程序的运行不可预料。

但是这种机制对于前端应用状态管理来说是很有帮助的,因为毕竟很多状态都会在组件之间进行共享,context 会给我们带来很大的方便。一些第三方的前端应用状态管理的库(例如 Redux)就是充分地利用了这种机制给我们提供便利的状态管理服务。但我们一般不需要手动写 context,也不要用它,只需要用好这些第三方的应用状态管理库就行了。

下一节:动手实现 Redux(一):优雅地修改共享状态

上一节:高阶组件(Higher-Order Components)

React.js 的 context的更多相关文章

  1. React.js 小书介绍

    React.js 小书 Github 关于作者 这是一本关于 React.js 的小书. 因为工作中一直在使用 React.js,也一直以来想总结一下自己关于 React.js 的一些知识.经验.于是 ...

  2. React.js学习知识小结(一)

    学习React也有半个月了吧,这里对所学的基础知识做个简单的总结.自己先是跟着官方文档学,差不多学了四五天,也跟着入门教程做了一个简单的小栗子.然后跟着阮一峰老师的教程上手了几个小Demo,后来在网上 ...

  3. 【React.js小书】动手实现 React-redux(五):Provider - 方志

    我们要把 context 相关的代码从所有业务组件中清除出去,现在的代码里面还有一个地方是被污染的.那就是 src/index.js 里面的 Index: 1234567891011121314151 ...

  4. React.js入门笔记

    # React.js入门笔记 核心提示 这是本人学习react.js的第一篇入门笔记,估计也会是该系列涵盖内容最多的笔记,主要内容来自英文官方文档的快速上手部分和阮一峰博客教程.当然,还有我自己尝试的 ...

  5. [转载]Sublime Text 3 搭建 React.js 开发环境

    [转载]Sublime Text 3 搭建 React.js 开发环境 Sublime有很强的自定义功能,插件库很庞大,针对新语言插件更新很快,配合使用可以快速搭建适配语言的开发环境. 1. babe ...

  6. Sublime Text 3 搭建 React.js 开发环境

    sublime有很强的自定义功能,插件库很庞大,针对新语言插件更新很快,配合使用可以快速搭建适配语言的开发环境. 1. babel 支持ES6, React.js, jsx代码高亮,对 JavaScr ...

  7. React.js小书总结

    (迁移自旧博客2017 08 27) 第一阶段 react的组件相当于MVC里面的View. react.js 将帮助我们将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合.嵌套,就成 ...

  8. 前端迷思与React.js

    前端迷思与React.js 前端技术这几年蓬勃发展, 这是当时某几个项目需要做前端技术选型时, 相关资料整理, 部分评论引用自社区. 开始吧: 目前, Web 开发技术框架选型为两种的占 80% .这 ...

  9. Web框架概述——React.js

    目前,在前端Web开发中,三大热门框架为React.js,Vue.js,Angular.js .当然,三大框架各有各的优缺点,这里就不多说了,下面我就针对前段时间所学的React框架做一下整体知识点的 ...

随机推荐

  1. VC实现趋势图绘制

    本文参考pudn上一个完整工程,在pudn搜索“50815867CurveDrawing”即可找到源代码.   上图是使用VS2010重写了该软件后的效果图,下面再贴出关键代码: // Plot.cp ...

  2. POJ3680 Intervals —— 区间k覆盖问题(最小费用流)

    题目链接:https://vjudge.net/problem/POJ-3680 Intervals Time Limit: 5000MS   Memory Limit: 65536K Total S ...

  3. Centos6.8更好yum源

    第一步:备份你的原镜像文件,以免出错后可以恢复. mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.back ...

  4. CSS:清除浮动

    周五去听css样式的培训,讲到float导致div不能被撑开的问题,特记录如下: 在写HTML代码的时候,如果有一个DIV作为外部容器,内部的DIV如果设置了float样式,则外部的容器DIV因为内部 ...

  5. Redis和StackExchange.Redis

    redis有多个数据库1.redis 中的每一个数据库,都由一个 redisDb 的结构存储.其中,redisDb.id 存储着 redis 数据库以整数表示的号码.redisDb.dict 存储着该 ...

  6. 【POJ 2411】 Mondriaan's Dream

    [题目链接] 点击打开链接 [算法] 很明显,我们可以用状态压缩动态规划解决此题 f[n][m]表示n-1行已经放满,第n行状态为m的合法的方案数 状态转移方程很好推 注意这题时限较紧,注意加一些小优 ...

  7. 【旧文章搬运】Idle进程相关的一些东西

    原文发表于百度空间,2009-05-13========================================================================== Idle进 ...

  8. 如何从kernel源码中查出版本号(转载)

    转载:http://m.android.tgbus.com/tgmobile/arc/174624.shtml 目前查版本号的方法都是在编译以后从rootfs里看的,难道从源码就看不到,一定要编译以后 ...

  9. 洛谷 - P1219 - 八皇后 - dfs

    https://www.luogu.org/problemnew/show/P1219 一开始朴素检查对角线就TLE了,给对角线编码之后压缩了13倍时间? 找了很久的bug居然是&&写 ...

  10. 二分匹配ZOJ3646

    //题意:类比线代里:把矩阵中的U看作[1],是否满足一个满秩矩阵 //利用二分匹配就是 //每一行都有相对应的列: #include<iostream> #include<stri ...