Context

返回目录

React Context 详解

Context 基本使用

CodeSandbox/Context

const ThemeContext = React.createContext('dark');
export const { Consumer, Provider } = ThemeContext;
// This is a HOC function.
// It takes a component...
export default function withTheme(Component) {
// ...and returns another component...
return function ThemedComponent(props) {
// ... and renders the wrapped component with the context theme!
// Notice that we pass through any additional props as well
return (
<Consumer>{theme => <Component {...props} theme={theme} />}</Consumer>
);
};
}
function Header({ children, theme }) {
return <h1 className={`header-${theme}`}>{children}</h1>;
}
// Use the withTheme HOC to inject the context theme,
// Without having to bloat our component to reference it:
export default withTheme(Header);

HoC 封装

Hooks 封装

老版本的 Context API

16.3 版本之前的 React 中的 Context 一直是实验特性,其使用也比较麻烦,这里我们简单回顾下,以方便使用老版本 React 的开发者。如果希望在组件中使用 Context,我们需要引入 contextTypesgetChildContextchildContextTypes 这三个属性:

  • getChildContext: 该函数是父组件的类函数之一,它会返回子函数中获取到的 this.context 的值内容,因此我们需要在这里设置子函数能够返回的属性信息。

  • childContextTypes: 该对象用于描述 getChildContext 返回值的数据结构,其会起到类似于 propTyps 这样的类型校验功能。

  • contextTypes: 该对象在子组件中用于描述父组件提供的上下文数据结构,可以将它看做子组件对于父组件的请求,同时也会起到类型检测的作用。

参考 React 官方示例,我们可以通过如下方式来使用 Context 跨层传递数据:

import PropTypes from 'prop-types';
class Button extends React.Component {
render() {
return (
<button style={{ background: this.context.color }}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: PropTypes.string
};
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
getChildContext() {
return { color: 'purple' };
}
render() {
const children = this.props.messages.map(message => (
<Message text={message.text} />
));
return <div>{children}</div>;
}
}
MessageList.childContextTypes = {
color: PropTypes.string
};

通过为 MessageList 组件添加 childContextTypes 与 getChildContext 属性,React 会自动将 getChildContext 返回的值传递到子组件树中。不过,React 官方并不建议我们大量使用 Context,原因概括为以下几点:

  • 老版本的 Context API 允许以 Props 方式透传,其问题在于破坏了组件本身的可移植性,或者说是分形架构,增强了组件间的耦合度。所谓的分形架构,即组件树中的任一部分能够被独立抽取使用,并且方便移植到其他组件树中。(参考诚身的回答)

  • 尽管其可以减少逐层传递带来的冗余代码,尽量的解耦和组件,但是当构造复杂时,我们也会陷入抽象漏洞,无法去判断 Context 到底是哪个父组件提供的。此时 Context 就像所谓的全局变量一样,大量的全局变量的使用会导致组件的不可以预测性,导致整个系统的鲁棒性降低。

  • Context 并不会触发组件重渲染,如果组件树中的某个组件的 shouldComponentUpdate 函数返回 false 而避免了子层组件的重渲染,那么新的 Context 值也就无法传递到子层组件,而导致目标组件无法保证自己每次都可以接收到更新后的 Context 值。

链接