React中,经过React组件能够很容易地追踪数据流。当你关注一个组件,你能够发现哪个props被传递了,这样使得你的应用很容被推断。react
在一些状况下,你想要传递数据经过组件树而不须要去手动在每一层传递。你能够直接使用强大的context API。react-router
为何不使用contextdom
大量的应用不须要使用context。函数
若是你但愿你的应用稳定,不要使用context。这是一个实验性的API在将来的版本中有可能会崩溃。this
若是你不熟悉state管理库就相似于Redux或者Mobx,不要使用context。对于不少实际的应用,这些库和它们的React绑定实现是很好的选择来管理state,这对于不少组件都有重大意义。解决问题最好的方案更像是Redux而不是context。spa
若是你不是一个有经验的React开发者,不要使用context。有更好的方式去实现功能经过使用props和state。code
若是你坚持使用context而无论这些警告,那么就请试图隔离你使用context在一个较小的范围而且避免直接使用context API为了当API改变的时候升级方便。component
怎样使用contextrouter
假设你拥有这样的一个结构:对象
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组件的有一个合适的样式。使用context,咱们能够自动传递属性经过树。
class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } Button.contextTypes = { color: React.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: React.PropTypes.string };
经过为MessageList组件(context提供者)添加childContextTypes属性和getChildContext方法,React会自动传递信息而且任何子树里的组件(在这个例子,Button组件)均可以获取到这个信息经过定义contextTypes。
若是contextTypes没有定义,那么context会是一个空对象。
父子联合
context也可让你建造一套可让父组件和子组件通讯的API。举个例子,一个这样运做的库叫作React Router v4:
import { 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组件传递一些信息,每个Link和Route均可以和包含的Router通讯。
在你使用相似的API建立组件的时候,思考是否有更灵活的替代方案。举个例子,你能够传递整个React组件做为props若是你喜欢。
在生命周期方法里引用context
若是在一个组件内部定义了contextTypes,下面的生命周期方法将会接收一个额外的参数,就是context对象:
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
在无状态的函数式组件里引用context
const Button = ({children}, context) => <button style={{background: context.color}}> {children} </button>; Button.contextTypes = {color: React.PropTypes.string};
更新context
不要这样作。
React有一个更新context的API,可是它实质上已经损坏你不该该使用它。
getChildContext函数当props或者state改变的时候会被调用。为了在context中更新数据,使用this.setState来触发本地的state更新。这样将会触发一个新的context而且改变会被子组件接收。
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: React.PropTypes.string };
问题在于,若是一个组件提供的context值改变了,使用那个值的子节点就不会更新若是中间的组件从shouldComponentUpdate返回了false。这样组件使用context就彻底失去了控制,所以基本没有什么方法能够可靠地更新context。这个博客有一个很好地解释关于为何这是一个问题以及你怎样避开它。