在一个典型的 React 应用中,数据是通过 props 属性由上向下(由父及子)的进行传递的,当遇到多个层级多个组件间共享一个props参数,这种树形的由上而下的传参方式就显得过于繁琐。
一般多组件嵌套传参写法如下:
// 底层
function ThemedButton(props) {
return <Button theme={props.theme} />;
}
// 中间
function Toolbar(props) {
// Toolbar 组件必须添加一个额外的 theme 属性
// 然后传递它给 ThemedButton 组件
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
// 顶层
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
当这种嵌套足够多和层级足够深时,传递参数就显得很麻烦。
context 函数提供了一种在组件之间共享参数的方式,不必通过组件树的每个层级显式地传递 props。
React.createContext
创建一个context组件,并设定默认值。语法如下:
const {Provider, Consumer} = React.createContext(defaultValue);
Provider:接收一个将要被往下层层传递的props,该值需在组件树最顶层设置。一个Provider可以关联到多个Consumers。
Consumer:接收一个函数作为子节点,函数接收当前 context 的值,即Provider提供的props值或创建时的默认值并返回到一个dom节点。传递给函数的props值等于组件树中最接近自身的Provider的props值。
(贴士)如果上层的组件树没有一个匹配的 Provider或没有定义Provider,而此时渲染了一个 Consumer 组件,那么该Consumer组件将得到 createContext()
的 defaultValue
。
demo代码演示,创建context:
// globalprops-context.js
import React from 'react';
export const globalAutoProps = {
data: {
itemID: "ID0001",
itemMsg: "Context共享参数"
}
};
export const globalPropsContext = React.createContext(
globalAutoProps.data // 默认值
);
组件树顶层:
// ContentProp.jsx
import React from 'react';
import ContentToolbar from './ContentToolbar.jsx';
import { globalPropsContext } from './globalprops-context.js';
class ContentProp extends React.Component {
constructor(props) {
super(props);
this.state = {
itemID: "ID0002",
itemMsg: "Context共享参数"
};
this.clickHandle = this.clickHandle.bind(this);
}
clickHandle(e) {}
render() {
// 此处value为必填值
return(
<globalPropsContext.Provider value={this.state}>
<ContentToolbar />
</globalPropsContext.Provider>
);
}
}
export default ContentProp;
组件树的中层:
// ContentToolbar.jsx
import React from 'react';
import ContentButton from './ContentButton.jsx'
class ContentToolbar extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return(<ContentButton />);
}
}
export default ContentToolbar;
组件树的底层:
// ContentButton.jsx
import React from 'react';
import { globalPropsContext } from './globalprops-context.js';
class ContentButton extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return(
<globalPropsContext.Consumer>
{({itemID, itemMsg}) => <div>{itemID},{itemMsg}</div>}
</globalPropsContext.Consumer>
);
}
}
export default ContentButton;
通过运行脚本会发现,demo中树顶层设置的value值:value={this.state} 无需通过组件树中间层的参数过渡传递,在组件树底层也可以共享得到。
贴士:
1. Content.Provider 允许作用于多个上下文,如:
<AContent.Provider>
<BContent.Provider></BContent.Provider>
</AContent.Provider>
2. 每当Provider的props值改变时, 作为Provider后代的所有Consumers都会重新渲染。 从Provider到其后代的Consumers传播不受shouldComponentUpdate方法的约束,因此即使祖先组件退出更新时,后代Consumer也会被更新。
不要仅仅为了避免在一个层级下的少数组件嵌套中传递 props 而使用 context,它是被用于在多个层级的多个组件需要访问相同数据的情景,如颜色主题、地点信息等。
另一种实现同样效果的技巧
通过直接提供Content和读取Content实现。通过 propTypes 与 childContextTypes :
// 顶层
import React from 'react';
import FluxDemo from './flux/FluxDemo.jsx';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
userinfo: "John"
};
}
render() {
return(
<div>
<FluxDemo userinfo={this.state.userinfo}/>
</div>
);
}
}
export default App;
// 中层 FluxDemo.jsx
import React from 'react';
import FluxView from './FluxView.jsx';
import PropTypes from 'prop-types';
class FluxDemo extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// 另一种定义 childContextTypes
//static childContextTypes = {
// userinfo: PropTypes.string
//}
// 读取content的函数
getChildContext() {
return {
userinfo: this.props.userinfo
};
}
render() {
return(
<div>
<FluxView />
</div>
);
}
}
// 关键代码
FluxDemo.propTypes = {
userinfo: PropTypes.string
}
// 关键代码
FluxDemo.childContextTypes = {
userinfo: PropTypes.string
};
export default FluxDemo;
// 底层
import React from 'react';
import PropTypes from 'prop-types';
class FluxView extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return(
<div>
{this.context.userinfo}
</div>
);
}
}
// 关键代码
FluxView.contextTypes = {
userinfo: PropTypes.string.isRequired
};
export default FluxView;
PropTypes的常用类型有:
PropTypes.array,
PropTypes.bool,
PropTypes.func,
PropTypes.number,
PropTypes.object,
PropTypes.string,
PropTypes.symbol,
// 任何东西都可以被渲染:numbers, strings, elements,或者是包含这些类型的数组(或者是片段)。
PropTypes.node,
// 一个 React 元素。
PropTypes.element,