React学习(10)—— 高阶应用:上下文(Context)
2017-04-10 00:00
519 查看
摘要: 绍React的“Context”(上下文)特性。
在使用React时,很容易在自定义的React组件之间跟踪数据流。当监控一个组件时,可以监控到那些props被传递进入组件了,这非常有利于了解数据流在什么地方出现了问题。
在某些情况下,开发者想要通过组件树直接传递数据,而不是在一层又一层的组件之间手工传递数据。此时,可以使用React的“context”特性接口来快速实现这个功能。
下面的这些情况,最好不要使用Context接口:
希望应用程序保持很好的健壮性:它目前只提供实验性的API接口并且很有可能在未来进行调整。
开发人员并不熟悉状态管理的外部库(比如 Redux 或 MobX):在许多实际应用系统中,这些外部库和React绑定在一起使用可以更加有效的管理状态,特别是许多组件统一使用相同的状态时。在绝大部分情况下,使用 Redux 作为技术解决方案比使用 context 更加合理。
非有经验的React开发人员:通常使用props和state就能够很好的实现功能, 即使需要层层传递数据也比在不清晰的情况下使用 Context接口要好。
如果并不担心以上问题,也不在意使用Context时出现的警告,还是强烈建议只在需要时小范围的使用上下文功能,或者通过一层包装来使用,以便在未来进行API调整时减少工作量。
在上面的例子中,在最外层组件手工传入一个
通过在
例子通过Router组件传递一些数据,每一个被
React提供一个更新Context的接口,但是它会从根本上破坏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"(翻墙),里面讨论了如果绕开这些问题。
相关文章推荐
- 项目经理案头手册学习系列【9、10】——资源强制进度计划、应用PERT编制进度计划
- Android开发学习笔记(10):NDK安装手顺及应用
- 复分析学习10——Liouville定理及其应用
- Spring与web MVC的整合——Spring的应用上下文管理 -- DispatcherServlet与ContextLoaderListener解释
- spring学习10-Application server-specific integration(具体的事物配置 结合特定的应用服务器)
- [MDX学习笔记之三]MDX的上下文(Context)
- java学习10--循环结构-for与while区别、无限循环、循环应用
- java中上下文(context)的学习总结
- 《TomCat与Java Web开发技术详解》(第二版) 第三章节的学习总结--利用Context元素来自定义web应用的存储位置
- 一步一步跟我学习lucene(10)---lucene搜索之联想词提示之suggest原理和应用
- iphone开发 Quratz 2D 学习 ---什么是 graphics context(图形上下文)
- PHP学习笔记10-MYSQL在PHP5中的应用
- 从零开始--系统深入学习android(理论-开发前准备-10.一个好的应用应该具备哪些方面-10.1易于访问)保留
- openerp学习笔记 context 的应用
- (4)事件处理——(10)事件处理上下文(Event handler context)
- iphone开发 Quratz 2D 学习 ---什么是 graphics context(图形上下文)
- 从零开始--系统深入学习android(理论-开发前准备-10.一个好的应用应该具备哪些方面-10.1易于访问)保留
- 数据结构与算法学习笔记——堆栈及其应用(10以内简单四则计算器)
- Qt Quick应用开发介绍 10-12(动态界面, 实践学习, 总结和扩展)
- Spring加载applicationContext.xml应用上下文的方式