
3.1 Context
在React中,父组件通常将数据作为props传递给子组件。如果需要跨层级传递数据,那么使用props逐层传递数据将会使开发变得复杂。同时,在实际开发中,许多组件需要一些相同的东西,如国际化语言配置、应用的主题色等,在开发组件的过程中也不希望逐级传递这些配置信息。
在这种情况下,可以使用React的Context特性。Context被翻译为上下文,如同字面意思,其包含了跨越当前层级的信息。
Context在许多组件或者开发库中有着广泛的应用,如react-redux使用Context作为Provider,提供全局的store,以及React Router通过Context提供路由状态。掌握Context将会对理解React Router起到极大的帮助作用。这里以图3-1来说明Context如何跨组件传递数据。
在图3-1中,左侧组件树使用了逐层传递props的方式来传递数据,即使组件B、组件C不需要关心某个数据项,也被迫需要将该数据项作为props传递给子组件。而使用Context来实现组件间跨层级的数据传递,数据可直接从组件A传递到组件D中。

图3-1 Context跨组件传递数据
在React v16.3及以上版本中,可使用React.createContext接口创建Context容器。基于生产者-消费者模式,创建容器后可使用容器提供方(一般称为Provider)提供某跨层级数据,同时使用容器消费方(一般称为Consumer)消费容器提供方所提供的数据。示例如下:


通过setState改变count的值,触发render渲染,Context.Provider会将最新的value值传递给所有的Context.Consumer。

在上例中,顶层组件App使用 CountContext.Provider将this.state.count的值提供给后代组件。App的子组件Toolbar不消费Provider所提供的数据,Toolbar的子组件Button使用CountContext.Consumer获得App所提供的数据count。中间层的Toolbar组件对数据跨层级传递没有任何感知。在单击“更新”按钮触发数据传递时,Toolbar中的“Toolbar render”信息不会被打印。每次单击“更新”按钮时,仅会打印“app render”与“Button render”,这是因为在Provider所提供的值改变时,仅Consumer会渲染,所以Toolbar中的“Toolbar render”不会被打印。
如果在Toolbar中也使用Provider提供数据,如提供的value为500:

则Button中的Consumer得到的值将为500。原因在于当有多个Provider时,Consumer将消费组件树中最近一级的Provider所提供的值。这作为React的一个重要特性,在React Router源码中被大量应用。
注意,如果不设置Context.Provider的value,或者传入undefined,则Consumer并不会获得创建Context时的defaultValue数据。创建Context时的defaultValue数据主要提供给没有匹配到Provider的Consumer,如果去掉App中的Provider,则Consumer所获得的值为1。
如果希望使用this.context方式获取Provider所提供的值,则可声明类的静态属性contextType(React v16.6.0)。contextType的值为创建的Context,如:

在React v16.3以前,不支持通过createContext的方式创建上下文,可使用社区的polyfill方案,如create-react-context等。
注意,组件的优化方法如shouldComponentUpdate或者React.memo不能影响Context值的传递。若在Button中引入shouldComponentUpdate,则会阻止Button更新:

改变Provider所提供的值后,依然会触发Consumer的重新渲染,结果与未引入shouldComponentUpdate时一致。