在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. angularjs 系列之$q和promise

    还是同一个项目,在项目中,发现多个controller之内有一个共同的服务器请求,当时只是不断的重复使用,如今,现在项目结束,代码开始走向了优化迭代的阶段: 首先,我的思路是把这个共同的请求,从con ...

  2. html怎样可是使文本框内容不可修改

    html怎样可是使文本框内容不可修改 <input type="text" readonly="readonly" onfocus="alert ...

  3. php判断是否是微信浏览器

    php判断是否是微信浏览器 直接上代码: <?PHP function is_wechat_browser(){ $user_agent = $_SERVER['HTTP_USER_AGENT' ...

  4. 泛型,注解,反射配合优化BaseDao的猜想

    package test; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.la ...

  5. type_traits.h

    type_traits.h // Filename: type_traits.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: ht ...

  6. C语言小程序(八)、统计字母个数

    这么简单的程序本不应贴在这里,但每写一篇博客,积分涨10分,距离摆脱千里之外的排名又进一步,相当于刷榜了,哈哈! #include <stdio.h> #include <strin ...

  7. Mybatis学习--日志

    学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/logging.html Logging Mybatis内置的日志工厂提供日志功能,具 ...

  8. HDU1114(完全背包装满问题)

    Piggy-Bank Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  9. PAT1106(BFS)

    PAT 1106 思路 BFS用在tree上,这一个题里主要关注的是用vector去保存每一个节点所连接的子节点,当BFS 时,一旦发现该节点下面没有子节点,这一层一定是最短的路径,然后用当前的层数去 ...

  10. SciTE for Ruby的配置

    转自:http://my.oschina.net/xsinger/blog/14229?catalog=71266 下载下面这个文件:http://scintilla.sourceforge.net/ ...