天天看點

React使用新版Context建構元件樹工具注入

一、倉庫位址

本文章基于[email protected],講解我是如何使用新版Context api做工具注入的。

github位址

二、為什麼要向元件注入工具

打個比方,在一個元件樹中,通常可能會有多個元件會使用到ajax請求伺服器擷取資料,這時候你就必須在每個元件中引入ajax相關的庫才能使用,如下:

import ajax from 'ajax'

export default class App extends Component {
  ...

  componentDidMount () {
    ajax.get('http://xxx.com/a')
      .then(res => {
          ...
      })
  }

  ...
}

           

而每個元件對于工具的依賴也不相同,那麼則會有相當多的庫引入語句。

// a.jsx
import ajax from 'ajax'

//b.jsx
import ajax from 'ajax'
import storage from 'storage'           

然而我并不想每一個元件在我需要用到這種基礎工具的時候都要去單獨引入這些工具,那麼對于多入口的react項目來說,我們可不可以在入口處讓工具存放在元件樹的頂層,需要的時候我就将頂層的工具注入到對應的子元件,然後通過this去通路對應的工具進行使用呢

// 通過引入一個注解就可以注入頂層放置的工具函數
import { injectMethods } from 'provider'

@injectMethods
export default class TestProvider extends Component {
  ...

  componentDidMount () {
    this.props.ajax.get('http://xxx.com/a')
      .then(res => {
          ...
      })

    this.props.storage.setItem('key', 'value')
  }

  ...
}
           

這樣我們就不需要關注元件的工具引入了,統一在頂層進行管理,當然你也可以注入其餘的全局屬性

三、實作

新版的React采取聲明式的方式使用Context api,這是

react官方Context用法的說明

,下面我們先看看我們的入口是如何将工具存儲在頂層的Provider中的

// 入口js

// provider webpack中定義了alias
import { setProvider } from 'provider'
import detectAgent from 'provider/detect-agent'
import dateFormat from 'provider/format/date-format'
import storage from 'provider/storage'
import urlutils from 'provider/url-utils'

// 頂層需要注入的方法
let providers = Object.assign(
  {},
  detectAgent,
  dateFormat,
  storage,
  urlutils
)

render(
  // Context.Provider注入方法,供子元件使用
  setProvider(App, providers),
  document.getElementById('app')
)
           
// provider/index.js

export const Context = React.createContext()

// 傳入根節點與基礎工具,采用Context.Provider對跟元件進行包裝
export const setProvider = (RootComponent, providers) => {
  return (
    <Context.Provider value={providers}>
      <RootComponent></RootComponent>
    </Context.Provider>
  )
}

/**
 * 用注解@的方式給子元件注入全局方法
 * @param {component} RealComponent 
 * 
 * 如: @injectMethods
 *     class TestComponent extends Component {}
 * 
 * 通過上面的方式就可将存儲在頂層的方法注入進元件的props屬性中
 */
export const injectMethods = (RealComponent) => {
  return class extends Component {
    render () {
      return (
        <Context.Consumer>
          { value => <RealComponent {...value} {...this.props}></RealComponent> }
        </Context.Consumer>
      )
    }
  }
}
           

在入口檔案中,我們會将方法統一放置在Context.Provider元件中,之後通過Context.Consumer元件去擷取Provider中存儲的value,将工具注入到子元件的props中,injectMethods是一個注解,如果你的一個元件需要擷取頂層的工具,直接在元件上進行注解就可以注入到props中了

import { injectMethods } from 'provider'

// 給TestProvider的props注入頂層的方法
@injectMethods
export default class TestProvider extends Component {
  componentDidMount () {
    // 任意元件都可通過injectMethods注入全局方法
    console.log('test-provider已向props注入全局方法')
    console.log(this.props)
  }

  render () {
    let { $dateFormat } = this.props;

    return (
      <div>
        <p>test-provider</p>
        <p>{ $dateFormat(new Date()) }</p>
      </div>
    )
  }
}
           

當然,這種方式還可以對統一對元件樹中的一些常量進行管理,需要的時候就注入到props中使用。

原文釋出時間為:2018年06月28日

原文作者:bug給我滾

本文來源: 

掘金 https://juejin.im/entry/5b3a29f95188256228041f46

如需轉載請聯系原作者

繼續閱讀