写了这么久的react,竟然这段时间才了解到这个context高阶api,可知我是多么无知且没有求知欲。从现在开始学习,永远都不会晚,加油哦
介绍
context译为‘上下文’,很多编程语言都有,react也有。
react常用context进行多级组件的数据传递。实现组件跨层级通信。
API
React.createContext
创建一个 Context 对象。
当 React 渲染一个订阅了这个
Context 对象的组件,这个组件会从组件树中离自身最近的
那个匹配的 Provider 中读取到当前的 context 值。
Context.Provider
Provider 接收⼀个 value 属性,传递给消费组件,允许消费
组件订阅 context 的变化。⼀个 Provider 可以和多个消费组
件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆
盖外层的数据。
当 Provider 的 value 值发⽣生变化时,它内部的所有消费组
件都会重新渲染。Provider 及其内部 consumer 组件都不受
制于 shouldComponentUpdate 函数,因此当 consumer 组
件在其祖先组件退出更新的情况下也能更新。
Context.Consumer
这里,React 组件也可以订阅到 context 变更。这能让你在函
数式组件中完成订阅 context。
这个函数接收当前的 context 值,返回⼀个 React 节点。传
递给函数的 value 值等同于往上组件树离这个 context 最近
的 Provider 提供的 value 值。如果没有对应的
Provider, value 参数等同于传递给 createContext() 的
defaultValue 。
Class.contextType
挂载在 class 上的 contextType 属性会被重赋值为⼀个由 React.createContext() 创建的 Context 对象。这能让你 使用 this.context 来消费最近 Context 上的那个值。你可 以在任何⽣命周期中访问到它,包括 render 函数中。
你只通过该 API 订阅单⼀context。
使用
流程:创建context > 获取Provider和Consumer > Provider提供 值 > Consumer消费值
创建context
创建文件themeContext.js
import React from 'react'
// react中通过context实现祖组件向后台组件跨层级传值
// 创建一个context对象,创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据
export const ThemeContext = React.createContext({themeColor: 'green'});
// Provider(生产者): 和他的名字一样。
// 用于生产共享数据的地方。生产什么呢? 那就看value定义的是什么了。value:放置共享的数据。
// <Provider value={/*共享的数据*/}>
/*里面可以渲染对应的内容*/
// </Provider>
export const ThemeProvider = ThemeContext.Provider;
// Consumer(消费者):他是专门消费供应商(Provider 上面提到的)产生数据。
// <Consumer>
// {value => /*根据上下文 进行渲染相应内容*/}
// </Consumer>
export const ThemeConsumer = ThemeContext.Consumer;
用Provider存入数据
import React, { Component } from 'react';
import { ThemeProvider, ThemeContext } from '../themeContext';
import ConsumerPage from './ConsumerPage';
import { Button } from 'antd';
class ContextPage extends Component {
constructor(props) {
super(props);
this.state = {
theme: {
themeColor: 'red'
}
};
}
changeColor = () => {
this.setState({
theme: this.state.theme.themeColor === 'red'? {themeColor: 'blue'} : {themeColor: 'red'}
})
}
static contextType = ThemeContext;
render() {
console.log('context page: ', this.context.themeColor);
return (
<ThemeProvider value={this.state.theme}>
{/* 直接从context里取值,取到的都是默认值 */}
<h3 style={{color: this.context.themeColor}}>
ContextPage, color: {JSON.stringify(this.state.theme)}
</h3>
<Button onClick={this.changeColor}>change color</Button>
<ConsumerPage/>
</ThemeProvider>
);
}
}
export default ContextPage;
用Consumer接收数据
import React, { Component, Fragment } from "react";
import { ThemeConsumer } from "../themeContext";
import ConsumerPage1 from "./ConsumerPage1";
class ConsumerPage extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<ThemeConsumer>
{value => (
<div className="bordered">
<h2 style={{ color: value.themeColor }}>Consumer Page</h2>
<ConsumerPage1 />
</div>
)}
</ThemeConsumer>
);
}
}
export default ConsumerPage;
注意事项
因为 context 会使⽤用参考标识(reference identity)来决定 何时进行渲染,这里可能会有一些陷阱,当 provider 的父组 件进行重渲染时,可能会在 consumers 组件中触发意外的渲 染。举个例例子,当每一次 Provider 重渲染时,以下的代码会 重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象。
class App extends React.Component {
render() {
return (
<Provider value={{something:'something'}}>
<Toolbar />
</Provider>
);
}
}
为了了防止这种情况,将 value 状态提升到父节点的 state里:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}>
<Toolbar />
</Provider>
);
}
}
总结
在React的官⽅文档中, Context 被归类为高级部分
(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。
不过,这并非意味着我们不需要关注 Context 。事实上,很
多优秀的React组件都通过Context来完成自己的功能,比如
react-redux的 ,就是通过 Context 提供一个
全局态的 store ,路由组件react-router通过 Context 管理路
由状态等等。在React组件开发中,如果用好 Context ,可以
让你的组件变得强大,而且灵活。
That's all. Thanks
脚踏实地,仰望星空。