在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。

很多优秀的React组件都通过Context来完成自己的功能:

  • 比如react-redux的,就是通过Context提供一个全局态的store;
  • 拖拽组件react-dnd,通过Context在组件中分发DOM的Drag和Drop事件;
  • 路由组件react-router通过Context管理路由状态等等。

在React组件开发中,如果用好Context,可以让你的组件变得强大,而且灵活。

简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。

使用props或者state传递数据,数据自顶下流。

适用于所有16.x版本(旧版本之后会被废弃)

如何使用Context

如果要Context发挥作用,需要用到两种组件,一个是Context生产者(Provider),通常是一个父节点,另外是一个Context的消费者(Consumer),通常是一个或者多个子节点。所以Context的使用基于生产者消费者模式。

对于父组件,也就是Context生产者,需要通过一个静态属性childContextTypes声明提供给子组件的Context对象的属性,并实现一个实例getChildContext方法,返回一个代表Context的纯对象 (plain object) 。

import React from 'react'
import PropTypes from 'prop-types' class ParentComponent extends React.Component { state = {
parentVal:'parentVal'
} // 声明Context对象属性
static childContextTypes = {
name: PropTypes.string,
fun: PropTypes.func
} // 返回Context对象,方法名是约定好的
getChildContext () {
return {
name: 'parent-msg',
fun: () => {
const {parentVal}=this.state;
console.log( 'from top msg', 'parent-state-parentVal:',parentVal )
}
}
} render () {
return (
<div>
<p>我是父级或者祖父</p>
</div>
) }
}

而对于Context的消费者,通过如下方式访问父组件提供的Context。

import React from 'react'
import PropTypes from 'prop-types' class ChildComponent extends React.Component {
// 声明需要使用的Context属性
static contextTypes = {
name: PropTypes.string,
fun: PropTypes.func
} render () {
const {name,fun} = this.context
console.log(name)
fun()
}
}

子组件需要通过一个静态属性contextTypes声明后,才能访问父组件Context对象的属性,否则,即使属性名没写错,拿到的对象也是undefined。

新版

请谨慎使用,因为这会使得组件的复用性变差。

使用context的通用的场景包括管理当前的locale,theme,或者一些缓存数据,这比替代方案要简单的多。

API

React.createContext

const MyContext = React.createContext(defaultValue);

创建一个Context对象。当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的上下文值。

Context.Provider

<MyContext.Provider value={/* 某个值 */}>

每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化。

当提供者的value值发生变化时,它内部的所有消费组件都会重新渲染。

提供者及其内部消费者组件都不受制shouldComponentUpdate函数,因此当消费者组件在其祖先组件退出更新的情况下也能更新。

Class.contextType

class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;

挂载在类上的contextType属性会被重赋值为一个由React.createContext()创建的Context对象。

这能让你使用this.context来消费最近Context上的那个值。你可以在任何生命周期中访问到它,包括渲染函数中。

可以使用static这个类属性来初始化你的contextType。

class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}

Context.Consumer

<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

传递给函数的value值等同于往上组件树离这个context最近的提供者提供的value值。如果没有对应的Provider,value参数等同于传递给createContext()的defaultValue。

注意:当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。

示例

关键语句 ( 类似与 eventBus 里的 bus.js 作为一个媒介,该句用于方便理解 )

createContext.js

import React from 'react';
export default React.createContext('createContext-test-defaultVal');

提供初始 context 值的组件

parent.js

import React from 'react';
import createContext from './createContext'; // 引入媒介 render() {
// 在 createContext 内部的 ComTest 组件使用 state 中的 name 值,
// 而外部的组件使用默认的值
return (
<div>
<createContext.Provider value={this.state.name}>
// 内部可以获取到分发的值
<ComTest fun={this.fun} />
<Tier0 />
</createContext.Provider>
<div>
<ComTest />
</div>
</div>
);
}

要获取context值的组件

只要在 createContext.Provider 里,不用管中间隔了多少层,只需要在使用context的组件内引入媒介即可

import React from 'react';
import createContext from './createContext'; // 引入媒介 class ComTest extends React.Component {
static contextType = createContext;
componentDidMount(){
console.log(this.context)
}
render(){
return (
<div>
<p>此处为获取数据的子组件 : {this.context}</p>
</div>
)
}
}
// ComTest.contextType = createContext; export default ComTest;

多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点。


// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({name: 'Guest',}); class App extends React.Component {
render() {
const {signedInUser, theme} = this.props; // 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
} function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
} // 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => ( // 对应 ThemeContext.Provider 的 value
<UserContext.Consumer>
{user => ( // 对应 UserContext.Provider 的 value
<Child user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}

官方文档:https://zh-hans.reactjs.org/docs/context.html#___gatsby

对 React Context 的理解以及应用的更多相关文章

  1. react中对于context的理解

    一.context旧版的基本使用 1.context的理解 当不想在组件树中通过逐层传递props或state的方式来传递数据时,可使用context来实现跨层级的组件数据传递. 2.context的 ...

  2. React Context 理解和使用

    写在前面 ​ 鉴于笔者学习此内容章节 React官方文档 时感到阅读理解抽象困难,所以决定根据文档理解写一篇自己对Context的理解,文章附带示例,以为更易于理解学习.更多内容请参考 React官方 ...

  3. 探索 Redux4.0 版本迭代 论基础谈展望(对比 React context)

    Redux 在几天前(2018.04.18)发布了新版本,6 commits 被合入 master.从诞生起,到如今 4.0 版本,Redux 保持了使用层面的平滑过渡.同时前不久, React 也从 ...

  4. React context基本用法

    React的context就是一个全局变量,可以从根组件跨级别在React的组件中传递.React context的API有两个版本,React16.x之前的是老版本的context,之后的是新版本的 ...

  5. structs2 对ActionContext valueStack stack context 的理解 图片实例

    structs2 对ActionContext valueStack stack context 的理解 ActionConext : The ActionContext is the context ...

  6. [React] Prevent Unnecessary Rerenders of Compound Components using React Context

    Due to the way that React Context Providers work, our current implementation re-renders all our comp ...

  7. React Context API

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

  8. React Hooks +React Context vs Redux

    React Hooks +React Context vs Redux https://blog.logrocket.com/use-hooks-and-context-not-react-and-r ...

  9. [译]React Context

    欢迎各位指导与讨论 : ) 前言 由于笔者英语和技术水平有限,有不足的地方恳请各位指出.我会及时修正的 O(∩_∩)O 当前React版本 15.0.1 时间 2016/4/25 正文 React一个 ...

随机推荐

  1. CSS基础语法和CSS经常用到的知识点总结

    1. [代码]css基础教程     CSS基础语法CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明.每条声明由一个属性和一个值组成.每个属性有一个值.属性和值被冒号分开.例如:下面这行代 ...

  2. Spring MVC的工作原理和机制

    Spring  MVC的工作原理和机制 参考: springMVC 的工作原理和机制 - 孤鸿子 - 博客园https://www.cnblogs.com/zbf1214/p/5265117.html ...

  3. delphi数据库的备份及还原

    实例应用1: //备份procedure TF_DataBaseBackUp.Btn_bfClick(Sender: TObject); var i:integer; begin if SaveDia ...

  4. javaScript-进阶篇(三)

    1.Window对象 window对象是BOM的核心,window对象指当前的浏览器窗口. window对象方法: 2.JavaScript 计时器 在JavaScript中,我们可以在设定的时间间隔 ...

  5. hihocoder-1285 智力竞赛(区间dp)

    智力竞赛 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi.小Ho还有被小Hi强拉来的小Z,准备组队参加一个智力竞赛.竞赛采用过关制,共计N个关卡.在第i个关卡中,小 ...

  6. codeforces 653A A. Bear and Three Balls(水题)

    题目链接: A. Bear and Three Balls time limit per test 2 seconds memory limit per test 256 megabytes inpu ...

  7. linux命令学习笔记(50):crontab命令

    前一天学习了 at 命令是针对仅运行一次的任务,循环运行的例行性计划任务,linux系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个 ...

  8. linux命令学习笔记(41):ps命令

    Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前 那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程 ...

  9. linux 在后台运行数据库导入导出命令

    nohup imp dbusername/password@orcl file=/home/20170928.dmp ignore=y log=/home/oracle/20170928.log fu ...

  10. bzoj 3779: 重组病毒 LCT+线段树+倍增

    题目: 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒. 实验在一个封闭 ...