这一节我们来介绍一个你可能永远用不上的 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. scrollView 代理方法的实现顺序的些许区别

  2. react项目中的注意点

    一.ES6 的编译方法 目前主流的浏览器还不支持ES6. 现在一般采用webpack 和 <script type="text/babel">对jsx  语法进行编译, ...

  3. Linux时间子系统之二:表示时间的单位和结构【转】

    本文转载自:http://blog.csdn.net/droidphone/article/details/7979295 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+] ...

  4. @Data 注解在实体类的使用可省去生成GET,SET方法

    你有没有觉得在每个实体类中都要生成get,set方法很麻烦, 要是需求的变化,需要再加一个字段,添加属性后则又要添加其get,set方法 现有一个很好用的注解能解决这个麻烦 1,idea中装上 lom ...

  5. Python之如果添加扩展包

    1.首先下载好你需要的扩展包 下载地址是http://www.lfd.uci.edu/~gohlke/pythonlibs/ 2.将你下载好的.whl文件放在你的python文件夹中的Lib\site ...

  6. Oracle:通过dbv查看数据文件是否有坏块

    我们备份的数据文件,可以通过oacle自带的dbv工具来查看是否是好的. 下面实验如下: 环境:oracle10.2.0.1 1.检查数据文件是否有坏块 [oracle@app orcl]$ dbv ...

  7. 【转载】AsyncTask用法

    在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...

  8. magento导入数据的方法

    导入演示数据 分两种情况处理. 如果你是用composer方式安装的 非常简单,二行命令搞定:在项目根目录下执行.我们的是在/var/www/magento2/下面. 安装演示数据 php bin/m ...

  9. position属性中的绝对定位和相对定位

    absolute(绝对定位):1.如果没有父级DIV,则会根据浏览器原始点去定位,而且跟他相邻的DIV会忽略它,定位后则可用TRBL(top,right,bottom,left)去布局.注意:TRBL ...

  10. 02_通过scrollview实现内容滚动

    在ScrollView里面放一个Button和TextView程序直接就挂了. ScrollView它只限制了有几个孩子,没限制有几个孙子.给Button和TextView套上一个爹LinearLay ...