在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. Luogu P1377 [TJOI2011]树的序:离线nlogn建二叉搜索树

    题目链接:https://www.luogu.org/problemnew/show/P1377 题意: 有一棵n个节点的二叉搜索树. 给出它的插入序列,是一个1到n的排列. 问你使得树的形态相同的字 ...

  2. 解决Eclipse和myeclipse在进行 html,jsp等 页面编辑时,自动格式化变丑的问题

    在eclipse和myelipse写JAVA代码时中使用ctrl+shift+f 快捷键自动排版省时又省力,排版后的代码规范美观又层次性,但在我们写jsp或html代码时,使用这个快捷键排版简直奇丑无 ...

  3. Java企业微信开发_07_JSSDK多图上传

    一.本节要点 1.1可信域名 所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名.这个域名必须要通过ICP备案,不然jssdk会配置失 ...

  4. SPOJ Query on a tree II (树剖||倍增LCA)(占位)

    You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, ...

  5. bzoj 4031: 小Z的房间 矩阵树定理

    bzoj 4031: 小Z的房间 矩阵树定理 题目: 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子.在一开始的时 ...

  6. POJ3621Sightseeing Cows

    Sightseeing Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10595   Accepted: 3632 ...

  7. 【C/C++】scanf,printf 函数

    摘自http://www.cplusplus.com 1. scanf 函数 int scanf ( const char * format, ... ); Parameters format C s ...

  8. WSGI服务与django的关系

    WSGI接口 wsgi是将python服务器程序连接到web服务器的通用协议.uwsgi是独立的实现了wsgi协议的服务器.   web服务器 服务端程序 简化版的WSGI架构 服务端程序(类似dja ...

  9. JavaScript编写代码时候没有提示

    如上图所示如果没有提示可能是箭头所指示的类型不正确.

  10. C#添加修改控件css样式

    一.添加属性 MyStyleSheet.Attributes.Add("href","/css/flostyle.css") 二.改变css样式 if (use ...