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