React做为前端最🔥的框架之一,可是有的时候咱们仅限于能用的阶段,有一些高级用法,咱们在平常开发中却不多涉足。可是一旦用起来,咱们就能发现它的方便和强大之处,咱们就会愈来愈发现咱们已经离不开它了!这就像是刚用React时,我心里是拒绝的,可是如今我已经离不开它了,愈来愈不能理解之前本身为何抱着JQuery不放呢!javascript
今天咱们重点讲一下Context这个高级API,以及如何封装它,让它更加易用!前端
Context 经过组件树提供了一个传递数据的方法,从而避免了在每个层级手动的传递 props 属性。java
在一个典型的 React 应用中,数据是经过props属性由上向下(由父及子)的进行传递的,但这对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI主题),这是应用程序中许多组件都所须要的。 Context 提供了一种在组件之间共享此类值的方式,而没必要经过组件树的每一个层级显式地传递 props 。react
简单说就是,当你不想在组件树中经过逐层传递props或者state的方式来传递数据时,可使用Context来实现跨层级的组件数据传递。markdown
假设咱们有一种场景,咱们有一个业务容器App,里面有一个组件容器Container,Container组件内包含一个Form表单,Form表单里面有一个提交按钮SubmitButton。假如使用props传递,咱们就不得不传递四层。框架
看到了吗?很方便吧!这里咱们使用Context,在组件能够直接经过context获取最顶层绑定的值,避免了一层层传递props的麻烦,也减小出错的可能性。ide
若是要Context发挥做用,须要用到两种组件,一个是Context生产者(Provider),一般是一个父节点,另外是一个Context的消费者(Consumer),一般是一个或者多个子节点。因此Context的使用基于生产者消费者模式。工具
Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。例如,在下面的代码中,咱们经过一个“theme”属性手动调整一个按钮组件的样式,使用context,咱们能够避免经过中间元素传递props。this
// 建立一个 theme Context, 默认 theme 的值为 light const ThemeContext = React.createContext('light'); function ThemedButton(props) { // ThemedButton 组件从 context 接收 theme return ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer> ); } // 中间组件 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 复制代码
看了上面的使用方式,有没有以为仍是有一些不爽,有没有简单一点的方式呢,或者能不能帮我封装一下呢? 固然能够,Javascript工程师是无所不能的!!!spa
首先是咱们的provider.js,这个就是咱们封装的context使用工具
import React, { Component } from 'react'; export const Context = React.createContext(); export class ContextProvider extends Component { render() { return ( <Context.Provider value={this.props.context}> {this.props.children} </Context.Provider> ); } } /** * 用注解的方式给子组件注入属性 */ export const injectContext = (contexts) => RealComponent => { return class extends Component { render() { return ( <Context.Consumer> {context => { // 将顶层的context分发到各层 let mapContext = {}; if(Array.isArray(contexts)) { contexts.map(item => { mapContext[item] = context[item]; }); } return ( <RealComponent {...mapContext} {...this.props} /> ) }} </Context.Consumer> ); } }; }; 复制代码
仍是举个栗子,来让你们明白上述封装的方法的方便之处。 假如要实现GrandParent -> Parent -> Son,从GrandParent组件传递属性到GrandSon组件,每一个组件都有一个独立的文件。
先看入口文件,咱们在入口文件进行绑定上下文,使用provider里面的ContextProvider类,这里咱们主要绑定了propA和propB。
// 入口文件 import React, { PureComponent } from 'react'; import { ContextProvider } from './provider'; import GrandParent from './GrandParent'; class Index extends PureComponent { render () { return ( <ContextProvider context={{ propA: 'propA', propB: 'propB' }}> <GrandParent /> </ContextProvider> ) } } 复制代码
Parent组件没什么特殊的
import React, { PureComponent } from 'react'; import Son from './Son'; class Index extends PureComponent { render () { return ( <Son /> ) } } 复制代码
Son组件是真正使用属性propA和propB的地方,咱们经过ES6的Decorator实现,很是方便。注入后,能够像props同样使用。
import React, { PureComponent } from 'react'; import { injectContext } from './provider'; import Son from './Son'; @injextContext(['propA', 'propB']) class Index extends PureComponent { render () { return ( <div> <span>propA为{this.props.propA}</span> <span>propB为{this.props.propB}</span> </div> ) } } 复制代码
在这一小节中,咱们主要讲了React的一个高级语法Context,并且为了使用方便,咱们封装了ContextProvider类和injextContext方法,使用时利用ES6的Decorator语法糖,很是简便。你们在平常开发中,也能够封装出一些这样的小工具,能够极大提高开发效率。
最后元旦快到了,祝你们新年快乐!!!
@Author: WaterMan