今天在学习styled-components的Theming
时,关于styled-components
对主题的实现与管理上提到,主要应用到了react
的context API,因此好好研读了一下官方文档,对该API作了以下记录。html
当咱们使用React
时,很容易的经过观察组件的props
来跟踪组件间的数据流流向,这种跟踪观察方式也让咱们很容易的去理解组件。 react
而有的时候,咱们不想让一个props
从最外层,经过组件一层一层的传递到目标组件上,这时就能够经过context
来直接实现咱们但愿的操做。git
假设有个以下的结构:github
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
,这期间穿越了Message
,而对Message
自己没有什么用。若是用context
的话,能够直接给到Button
组件上,以下:redux
const PropTypes = require('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
(Context宿主)添加childContextTypes
和getChildContext
,能够实如今该组件子结构下的全部组件(e.g. Button)直接经过定义contextTypes
来获取。 安全
若是未定义contextTypes
的话,context
是一个空对象。函数
一旦组件定义了contextTypes
之后,如下的勾子中就会获得一个附加的参数——context
对象:学习
无状态组件一样能够经过给函数定义contextTypes
属性的方式,让组件拥有获取context
的能力,例如:ui
const PropTypes = require('prop-types'); const Button = ({children}, context) => <button style={{background: context.color}}> {children} </button>; Button.contextTypes = {color: PropTypes.string};
不要更新Context!this
React
虽然有提供关于更新context
的API,但不建议去使用。
若是想用的话,能够看下面的这个例子。getChildContext
方法会在state
或props
更新时被调用,能够经过局部状态的更新进而来更新context
。当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
更新了,其下使用该context
的子组件可能由于某个父组件的shouldComponentUpdate
返回false
而不作状态更新。这就彻底不符合经过使用context
来控制组件状态更新的初衷,因此证实使用context
来管理组件状态不太靠谱。
这里有篇博客关于介绍如何安全的使用context
的。
绝大多数的应用程序是不须要使用context
的。
若是你想要你的应用稳定,就不要使用它,这是一个实验性的API,在将来的版本更新中颇有可能会被弃掉。
context最好的使用场景是隐式的传入登陆的用户,当前的语言,或者主题信息。要否则全部这些可能就是全局变量,可是context让你限定他们到一个单独的React树里。
若是项目对数据管理较为复杂,推荐使用相似于redux或mobX这样的状态管理库,而不要使用context
。
记录的过程是一种成长,欢迎你们关注个人github。