React 中的新舊 Context 簡單對比

rccoder發表於2018-02-26

原文地址:https://github.com/rccoder/blog/issues/30

媽媽,我再也不會濫用 Redux 了

前言

context 是各種前後端框架中經常會用到的一個概念,著名 Node 框架 Koa 更是把 context 玩的盡興。React 在很早之前就有 context 的概念,雖然是一個實驗性質的 API,但 react-redux、react -router 等框架類庫卻把它玩了個夠。

React 中爺爺和孫子,甚至是重孫之間傳遞值或者事件一直是個比較麻煩的事情,隨著 Redux 等狀態管理類庫的出現,大家紛紛開始用這種框架去解決這種隔代傳資訊的問題,並且在或大或小的專案中都開始使用。

一回喜,二回憂,在前端視資源體積為金子的情況下 “濫用 Redux” 的情況越來越多。

React 新版本中的 context 終於要轉正了,並且經過了重新的思考與沉澱,與之前 context 在設計哲學上不一樣,但解決的卻是同一個問題。這樣,或許資料流真的不是很多的專案或許可以真的擺脫一下 Redux 等去試試自帶的 Context 了。

本文將介紹新老 Context 的一些用法和自己的一些思考。

子孫傳值問題

首先看一下最簡單的 爺爺給孫子 傳值的問題:

class Children extends React.Component {
  render() {
    return (
      <div>{ this.props.text }</div>
      )
    }
}

class Parent extends React.Component {
  render() {
    return (
      <Children text="this.props.text"/>
     )
  }
}

class GrandParent extends React.Component {
  render() {
    return (
      <Parent text="Hi, my baby"/>
    )
  }
}
複製程式碼

這幾行程式碼中都是手動的讓 text 一輩一輩 的往下傳,如果是傳給重孫的話還需要繼續手動往下傳。

利用 Context 就能讓這種資訊自動的傳遞,不再讓 中間輩 去擔任訊息傳遞人,做一些沒太大意義的事。

老 Context

const PropTypes = require('prop-types');

class Children extends React.Component {
  static contextTypes = {
    text: PropTypes.string
  }
  render() {
    return (
      <div>{ this.context.text }</div>
    )
  }
}

class Parent extends React.Component {
  render() {
    return (
      <Children/>
    )
  }
}

class GrandParent extends React.Component {
  static childContextTypes = {
    text: PropTypes.string,
  }
  getChildContext() {
    return {
      text: 'Hi, my baby'
    }
  }
  render() {
    return (
      <Parent text="Hi, my baby" />	
    )
  }
}
複製程式碼

GrandParent 上通過 getChildContext 給 context 物件新增了 text 的屬性,這個屬性可以在 GrandParent 的任何一個子孫(子元件)中訪問。

同時,為了方便在各種生命週期中使用 context,部分生命週期都給 context 留了介面,具體可以參考 Referencing Context in Lifecycle Methods

新 Context

老式的 context 和依靠中間輩一層層去傳遞資料相比確實是酷酷的,但總感覺不 React。或許這也是為什麼之前一直不建議使用的原因吧。

同時還有一個比較?的 context 更新問題,更多可以參考 How to safely use React context

在 React 16.3 中 新版本的 Context 出爐了,感興趣的可以參考 rfc提案

新版 context 之下上面的程式碼這樣寫:

const AppContext = React.createContext();


class Children extends React.Component {
  render() {
    return (
      <AppContext.Consumer>
        {
          context => {
            return (
              <div>{context.text}</div>
            )
          }
        }
       </AppContext.Consumer>
     )
   }
}

class Parent extends React.Component {
  render() {
    return (
      <Children />
    )
  }
}

class GrandParent extends React.Component {
  render() {
    return (
      <AppContext.Provider
        value={{
          text: 'Hi, my baby'
        }}
       >
         <Parent />
       </AppContext.Provider>
     )
   }
}
複製程式碼

新版 Context 用 createContext 去初始化一個 context,返回的物件中有 providerconsumer 方法。provider 是以外層容器的方式去包裹住 context 要作用的最外層元件(使用過 Redux 的同學對這點應該有種似曾相似的感覺),然後需要使用到 context 的時候需要用 consumer 去包裹一下。需要注意的是 consumer 包裹的裡面的寫法,不是普通的元件。

雖然 context 可以是一個 Object,但還是避免不了業務邏輯中會出現多個 context 的問題,consumerprovider 一一對應的模式會造成花式巢狀地獄,可以使用偉大社群產生的 react-context-composer 對 context 進行 composer,原始碼也非常簡單易懂。

Context 會讓 Redux 消失嗎?

不會,解決的終極問題不完全一樣!

Redux 解決的是大型軟體架構中資料流傳輸的問題;context 解決的是子孫之間方便資料互動的問題。有一定的相似性,但不屬於同等性質。

延伸閱讀

題外

文章內容第一時間更新地址為:https://github.com/rccoder/blog/issues/30。為保證看到的是最新的,交流 & 閱讀優先點選 這裡

相關文章