对 React Context 的理解以及应用
在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 的理解以及应用的更多相关文章
- react中对于context的理解
一.context旧版的基本使用 1.context的理解 当不想在组件树中通过逐层传递props或state的方式来传递数据时,可使用context来实现跨层级的组件数据传递. 2.context的 ...
- React Context 理解和使用
写在前面 鉴于笔者学习此内容章节 React官方文档 时感到阅读理解抽象困难,所以决定根据文档理解写一篇自己对Context的理解,文章附带示例,以为更易于理解学习.更多内容请参考 React官方 ...
- 探索 Redux4.0 版本迭代 论基础谈展望(对比 React context)
Redux 在几天前(2018.04.18)发布了新版本,6 commits 被合入 master.从诞生起,到如今 4.0 版本,Redux 保持了使用层面的平滑过渡.同时前不久, React 也从 ...
- React context基本用法
React的context就是一个全局变量,可以从根组件跨级别在React的组件中传递.React context的API有两个版本,React16.x之前的是老版本的context,之后的是新版本的 ...
- structs2 对ActionContext valueStack stack context 的理解 图片实例
structs2 对ActionContext valueStack stack context 的理解 ActionConext : The ActionContext is the context ...
- [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 ...
- React Context API
使用React 开发程序的时候,组件中的数据共享是通过数据提升,变成父组件中的属性,然后再把属性向下传递给子组件来实现的.但当程序越来越复杂,需要共享的数据也越来越多,最后可能就把共享数据直接提升到最 ...
- React Hooks +React Context vs Redux
React Hooks +React Context vs Redux https://blog.logrocket.com/use-hooks-and-context-not-react-and-r ...
- [译]React Context
欢迎各位指导与讨论 : ) 前言 由于笔者英语和技术水平有限,有不足的地方恳请各位指出.我会及时修正的 O(∩_∩)O 当前React版本 15.0.1 时间 2016/4/25 正文 React一个 ...
随机推荐
- angularjs 系列之$q和promise
还是同一个项目,在项目中,发现多个controller之内有一个共同的服务器请求,当时只是不断的重复使用,如今,现在项目结束,代码开始走向了优化迭代的阶段: 首先,我的思路是把这个共同的请求,从con ...
- html怎样可是使文本框内容不可修改
html怎样可是使文本框内容不可修改 <input type="text" readonly="readonly" onfocus="alert ...
- php判断是否是微信浏览器
php判断是否是微信浏览器 直接上代码: <?PHP function is_wechat_browser(){ $user_agent = $_SERVER['HTTP_USER_AGENT' ...
- 泛型,注解,反射配合优化BaseDao的猜想
package test; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.la ...
- type_traits.h
type_traits.h // Filename: type_traits.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: ht ...
- C语言小程序(八)、统计字母个数
这么简单的程序本不应贴在这里,但每写一篇博客,积分涨10分,距离摆脱千里之外的排名又进一步,相当于刷榜了,哈哈! #include <stdio.h> #include <strin ...
- Mybatis学习--日志
学习笔记,选自Mybatis官方中文文档:http://www.mybatis.org/mybatis-3/zh/logging.html Logging Mybatis内置的日志工厂提供日志功能,具 ...
- HDU1114(完全背包装满问题)
Piggy-Bank Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- PAT1106(BFS)
PAT 1106 思路 BFS用在tree上,这一个题里主要关注的是用vector去保存每一个节点所连接的子节点,当BFS 时,一旦发现该节点下面没有子节点,这一层一定是最短的路径,然后用当前的层数去 ...
- SciTE for Ruby的配置
转自:http://my.oschina.net/xsinger/blog/14229?catalog=71266 下载下面这个文件:http://scintilla.sourceforge.net/ ...