您的位置:首页 > Web前端 > React

React学习(10)—— 高阶应用:上下文(Context)

2017-04-10 00:00 519 查看
摘要: 绍React的“Context”(上下文)特性。

Context

在开篇之前需要额外说明的是,最新发布的React v15.5版(2017年4月9号发布)的React.PropTypes已经取消,最新版本使用 prop-types库 来定义
contextTypes
,简而言之就是将参数定义放置到上下文中执行,由Context引入一个外部包来实现。

在使用React时,很容易在自定义的React组件之间跟踪数据流。当监控一个组件时,可以监控到那些props被传递进入组件了,这非常有利于了解数据流在什么地方出现了问题。

在某些情况下,开发者想要通过组件树直接传递数据,而不是在一层又一层的组件之间手工传递数据。此时,可以使用React的“context”特性接口来快速实现这个功能。

为什么建议不要使用Context

在看这篇文章之前首先需要强调,在任何情况下React官方都不建议使用Context,也许未来会组件稳定,但是目前还处于实验期。

下面的这些情况,最好不要使用Context接口:

希望应用程序保持很好的健壮性:它目前只提供实验性的API接口并且很有可能在未来进行调整。

开发人员并不熟悉状态管理的外部库(比如 Redux MobX):在许多实际应用系统中,这些外部库和React绑定在一起使用可以更加有效的管理状态,特别是许多组件统一使用相同的状态时。在绝大部分情况下,使用 Redux 作为技术解决方案比使用 context 更加合理。

非有经验的React开发人员:通常使用props和state就能够很好的实现功能, 即使需要层层传递数据也比在不清晰的情况下使用 Context接口要好。

如果并不担心以上问题,也不在意使用Context时出现的警告,还是强烈建议只在需要时小范围的使用上下文功能,或者通过一层包装来使用,以便在未来进行API调整时减少工作量。

如何使用Context

假设有下面这样一个组件结构:

class Button extends React.Component {
render() {
return (
<button style={{background: this.props.color}}>
{this.props.children}
</button>
);
}
}

class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button color={this.props.color}>Delete</Button>
</div>
);
}
}

class MessageList extends React.Component {
render() {
const color = "purple";
const children = this.props.messages.map((message) =>
<Message text={message.text} color={color} />
);
return <div>{children}</div>;
}
}

在上面的例子中,在最外层组件手工传入一个
color
属性参数来指定
Button
组件的颜色。如果使用Context特性,我们可以直接将属性自动的传递给整个组件树:

const PropTypes = require('prop-types');

class Button extends React.Component {
render() {
// 注意this.context.color
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}

// 限定color属性只接收string类型的参数
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 {
// 在后续组件中设定一个Context的值
getChildContext() {
return {color: "purple"};
}

render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}

//限定子组件的color值只接收string类型的参数
MessageList.childContextTypes = {
color: PropTypes.string
};

通过在
MessageList
组件(Context的制定者)中增加
childContextTypes
getChildContext
,React会自动将这个指定的context值传递到所有子组件中(比如例子中的
Button
组件),而子组件也可以定义一个
contextTypes
来指定接收context的内容。如果未定义子组件的
contextTypes
,那么调用
context
只能得到一个空对象。

父子组件耦合

Context特性还可以让开发人员快速构建父组件与子组件之间的联系。例如在 React Router V4 包中:

import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>

<hr />

<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);

例子通过Router组件传递一些数据,每一个被
Router
包含的
Link
Route
都可以直接通信。但是建议在使用这些API构建组件时,先思考是否还有其他更清晰的实现方式。例如可以使用回调的方式去组合组件。

在生命周期方法中引入Context

如果在某个组件上定义了
contextTypes
,下面这些生命周期方法将会接收到额外的参数——
context
对象。我们这里这样调整参数接口:

constructor(props, context)


componentWillReceiveProps(nextProps, nextContext)


shouldComponentUpdate(nextProps, nextState, nextContext)


componentWillUpdate(nextProps, nextState, nextContext)


componentDidUpdate(prevProps, prevState, prevContext)


在无状态的方法性组件中引入Context

无状态的方法性组件也可以引入Context,前提是给组件定义了
contextTypes
。下面的代码展示了在无状态的组件——
Button
中引入context的表达式:

const PropTypes = require('prop-types');

const Button = ({children}, context) =>
<button style={{background: context.color}}>
{children}
</button>;

Button.contextTypes = {color: PropTypes.string};


更新Context

首先,千万不要更新Context。

React提供一个更新Context的接口,但是它会从根本上破坏React的结构所以建议不要使用他。

getChildContext
在state或props变更时会被调用。为了更新context中的数据可以使用
this.setState
方法来触发变更,触发之后context的更新会被子组件接收到。

const PropTypes = require('prop-types');

class MediaQuery extends React.Component {
constructor(props) {
super(props);
this.state = {type:'desktop'};
}

getChildContext() {
return {type: this.state.type};
}

componentDidMount() {
const checkMediaQuery = () => {
const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
if (type !== this.state.type) {
this.setState({type});
}
};

window.addEventListener('resize', checkMediaQuery);
checkMediaQuery();
}

render() {
return this.props.children;
}
}

MediaQuery.childContextTypes = {
type: PropTypes.string
};

这里的问题在于,如果一个context在组件变更时才产生,接下来如果中间某个组件的
shouldComponentUpdate
方法返回fasle值,那么后续组件无法从context中得到任何值。所以,如果使用context来维护管理状态,那么就需要从全局去控制组件,这和React单向数据流和组件化的思路有些背道而驰。而且随着应用的扩展以及人员的更变,全局管理状态会越来越难。如果你还想了解更多关于context的问题,可以阅读这篇博客文章——“How To Safely Use React Context"(翻墙),里面讨论了如果绕开这些问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  React ReactJS JSX
相关文章推荐