前言我们先看一下React中,父子组件通信的机制,父子组件的通信是通过props进行数据的传递:1、父组件向子组件传递数据(状态)时,是在调用子组件的时候通过参数传递给子组件,子组件通过this.props进行接收;2、子组件如果更改父组件的一些属性,则是通过父组件定义的方法来传递给子组件,子组件调用更改;3、如果父组件想要更改子组件的一些状态时,通过ref进行标记,可以获取子组件的所有信息,从而调用子组件的方法和值;但是,如果层级很多呢,是否需要多个props进行逐层的传递?答案是否定的,React的advanced(高级)中指出了context,优雅的解决这个问题。初识Context我们知道,在JS中context指的是函数的执行上下文,函数被调用时,this指向谁,谁就是当前的执行上下文;react中的context是什么呢?官方文档给出:Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。文档也没具体给出context到底是什么,而是告诉我们context能干什么,也就是说,如果我们不想通过props实现组件树的逐层传递数据,则可以使用context实现跨层级进行数据传递!如何使用Contextcontext api给出三个概念:React.createContext()、Provider、Consumer;React.createContext()这个方法用来创建context对象,并包含Provider、Consumer两个组件 <Provider />、<Consumer />const {Provider, Consumer} = React.createContext();Provider数据的生产者,通过value属性接收存储的公共状态,来传递给子组件或后代组件eg:<Provider value={/* some value /}>Consumer数据的消费者,通过订阅Provider传入的context的值,来实时更新当前组件的状态eg: <Consumer> {value => / render something based on the context value */}</Consumer>值得一提的是每当Provider的值发生改变时, 作为Provider后代的所有Consumers都会重新渲染props单向数据流动:如果觉得Props传递数据很繁琐,可以采用context,进行跨组件传递数据再最外层的组件上,通过生产者Provider组件进行包裹,并存储共享数据到value中,当然可以是任何数据类型。后带需要用到共享数据的组件均可通过Consumer进行数据获取。与旧版本比较新版的context是即react16.3以后进行升级的,但是16.3以前的context却不是那么好用了,同时它还被赋予“实验性”帽子的称号。旧版本被赋予生产者和消费者的概念:对于父组件来说,也就是生产者,需要声明childContextTypes对象,来提供给子组件需要用到的公共数据;其次,还需要实例化getChildContext方法,返回一个纯对象,一定要注意的是父组件在getChildContext方法中定义数据的时候,一定要先在childContextTypes中进行声明,否则会报错;用代码描述是:父组件:// 父组件声明context数据childContextTypes = { orderVal: PropTypes.string, changeVal: PropTypes.func}; // 实例化getChildContext方法,返回一个纯对象getChildContext() { return { orderVal: this.state.orderVal } }; 子组件: // 子组件则声明需要使用的Context属性static contextTypes = { orderVal: PropTypes.string};// 通过this.context获取父组件的属性render(){ const { orderVal } = this.context; return ( <div>{orderVal}</div> )}如果某个组件更改了父组件的某个属性值,那么父组件将会重新render组件树,如果期间有一个子组件没有用到context中的这个属性,并定义了shouldComponentUpdate,那和这个子组件的子孙组件就不会重新render,这样就会很危险,而新版本的context api却弥补了这个问题,即使其中的某个组件没有数据更新,也会影响到子孙组件,就是通过一个Provider来支持多个Consumer子组件的数据传递;解析React-redux中的context1) 众所周知,react-redux是链接react、redux的桥梁,我们来解剖一下到底react-redux是怎么搭这座桥梁的;用redux来实现数据的传递:那么,使用Redux不是不可以,只不过用起来着实麻烦,需要一直订阅store里的数据,每次都需要写一遍,为了方便,Redux的作者封装了React专用的库react-redux;可见,react-redux实际上是通过Provider组件和connect方法进行连接react和redux,那么他们到底本质上就是通过Context来传递数据React-router中的context同样,我们来看一下React-router中是怎么用context的看一下Router组件的部分代码片段,react-router-dom用的还是react-router3版本的Router组件,其中的context采用也是旧版的写法,以后可能会升级!var Router = function (_React$Component) { … // Router组件扩展一个getChildContext方法,为子组件提供一个带有router属性的context // 监听history,history发生变化,并重新对route进行赋值 Router.prototype.getChildContext = function getChildContext() { return { router: _extends({}, this.context.router, { history: this.props.history, route: { location: this.props.history.location, match: this.state.match } }) }; };… Router.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { warning(this.props.history === nextProps.history, “You cannot change <Router history>”); }; … Router.prototype.render = function render() { var children = this.props.children; return children ? React.Children.only(children) : null; }; return Router;}(React.Component);Router.propTypes = { history: PropTypes.object.isRequired, children: PropTypes.node};Router.contextTypes = { router: PropTypes.object};Router.childContextTypes = { router: PropTypes.object.isRequired};export default Router;同样,Route组件、Link组件等,都是通过Context来构建的,通过context来共享router,并匹配当前路由的路径,来动态加载子组件;但是,路由只会给子组件共享context,那如果是子孙组件,怎么办呢?也给他包一层router,这样不也就能共享context了吗?那么我们怎么在包一层呢?答案: withRouterwithRouter用来修饰的这个UI组件,会在外层添加一层Route,这样就会共享context的状态了,所以无论我们在什么地方来进行路由切换,都需要包一层withRouter的原因了!context需谨慎使用虽说新版的context已经很好用了,但是官方还是没有大面积的去推广,可能有一些其他的问题,比如多个context的管理,但是我们还是很看好,他的未来,毕竟react-redux、react-router还在用