1. 程式人生 > >深入react 高階元件

深入react 高階元件

高階元件: 返回值是一個函式的那麼一個函式 基本算是個類工廠方法 W (WrappedComponent) 是被包裹的 React.Component;而函式返回的 E (Enhanced Component) 則是新得到的 HOC,也是個 React.Component 使用場景:
  • 屬性代理:由 HOC 操縱那些被傳遞給被包裹元件 W 的 props
  • 繼承反轉:HOC 繼承被包裹元件 W

hoc能做什麼 在大的維度上 HOC 能用於:
  • 程式碼重用和邏輯抽象
  • render 劫持
  • state 抽象和操縱
  • 操縱屬性(props)
屬性代理 const propsProxy = (props)=>( WrappedComponent)=>{ return class PP extends React.Component {    render() {      return <WrappedComponent {...props }{..this.props}/>    }  } } 此處關鍵的部分在於 HOC 的 render() 方法
返回 了一個被包裹元件的 React Element。同時,將 HOC 接受到的屬性傳遞給了被包裹的元件,因此稱為 “屬性代理” 可以用屬性代理做些什麼?
  • 操縱屬性
  • 通過 refs 訪問例項
  • 抽象 state
  • 包裹元件
可以對傳遞給被包裹元件的屬性進行增刪查改。但刪除或編輯重要屬性時要謹慎,應合理設定 HOC 的名稱空間以免影響被包裹元件。 操縱屬性 例子:增加新屬性。應用中通過 this.props.user 將可以得到已登入使用者
const ppHOC=(currentLoggedInUser)=>(WrappedComponent) =>{  return class PP extends React.Component {    render() {      const newProps = {        user: currentLoggedInUser      }      return <WrappedComponent {...newProps}/>    }  }}
通過 refs 訪問例項 可以通過  ref  訪問到  this (被包裹元件的例項),但這需要  ref  所引用的被包裹元件執行一次完整的初始化 render 過程,這就意味著要從 HOC 的 render 方法中返回被包裹元件的元素,並讓 React 完成其一致性比較過程,而  ref  能引用該元件的例項就好了。 例子:下例中展示瞭如何通過 refs 訪問到被包裹元件的例項方法和例項本身 可用在應用echarts等第三方庫時候的場景
function refsHOC(WrappedComponent) {  return class RefsHOC extends React.Component {    proc(wrappedComponentInstance) {      wrappedComponentInstance.method()    }        render() {      const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})      return <WrappedComponent {...props}/>    }  }}
該高階元件可以作為一個元件的方法使用 抽象 state 通過提供給被包裹元件的屬性和回撥,可以抽象 state,這非常類似於 smart 元件是如何處理 dumb 元件的。
例子:在下面這個抽象 state 的例子裡我們簡單的將 value 和 onChange 處理函式從 name 輸入框中抽象出來。
function ppHOC(WrappedComponent) {  return class PP extends React.Component {    constructor(props) {      super(props)      this.state = {        name: ''      }            this.onNameChange = this.onNameChange.bind(this)    }    onNameChange(event) {      this.setState({        name: event.target.value      })    }    render() {      const newProps = {        name: {          value: this.state.name,          onChange: this.onNameChange        }      }      return <WrappedComponent {...this.props} {...newProps}/>    }  }} 用起來可能會是這樣的: @ppHOCclass Example extends React.Component {  render() {    return <input name="name" {...this.props.name}/>  }} 於是這個輸入框就自動成為了一個受控元件。 包裹元件 可以利用元件的包裹,實現樣式定義、佈局或其他目標。一些基礎用法可以由普通的父元件完成(參閱附錄B),但如前所述,用 HOC 可以更加靈活。 例子:為定義樣式而實現的包裹 function ppHOC(WrappedComponent) {  return class PP extends React.Component {    render() {      return (        <div style={{display: 'block'}}>          <WrappedComponent {...this.props}/>        </div>      )    }  }} 繼承反轉 function iiHOC(WrappedComponent) {  return class Enhancer extends WrappedComponent {    render() {      return super.render()    }  }} 被返回的 HOC 類(強化過的類) 繼承 了被包裹的元件。之所以被稱為“繼承反轉”是因為,被包裹元件並不去繼承強化類,而是被動的讓強化類繼承。通過這種方式,兩個類的關係看起來 反轉 了。 繼承反轉使得 HOC 可以用   this   訪問被包裹元件的例項,這意味著 可以訪問 state、props、元件生命週期鉤子,以及 render 方法 可以用繼承反轉做些什麼?
  • render 劫持
  • 操縱 state
render 劫持 稱之為“render 劫持”是因為 HOC 控制了被包裹元件的 render 輸出,並能對其做任何事情。 在 render 劫持中可以:
  • 在任何 render 輸出的 React Elements 上增刪查改 props
  • 讀取並修改 render 輸出的 React Elements 樹
  • 條件性顯示元素樹
  • 出於定製樣式的目的包裹元素樹(正如屬性代理中展示的)
就如我們之前學到的,繼承反轉 HOC 不保證處理完整的子樹,這意味著 render 劫持技術有一些限制。經驗法則是,藉助於 render 劫持,可以不多不少的操作被包裹元件的 render 方法輸出的元素樹。如果那個元素數包含了一個函式型別的 React Component,那就無法操作其子元件(被 React 的一致性比較過程延遲到真正渲染到螢幕上時)。 例子1:條件性渲染 function iiHOC(WrappedComponent) {  return class Enhancer extends WrappedComponent {    render() {      if (this.props.loggedIn) {        return super.render()      } else {        return null      }    }  }}
例子2:修改 render 輸出的元素樹 function iiHOC(WrappedComponent) {  return class Enhancer extends WrappedComponent {    render() {      const elementsTree = super.render()      let newProps = {};      if (elementsTree && elementsTree.type === 'input') {        newProps = {value: 'may the force be with you'}      }      const props = Object.assign({}, elementsTree.props, newProps)      const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children)      return newElementsTree    }  }} 本例中,如果由 render 輸出的被包裹元件有一個 input 頂級元素,就改變其 value。 可以在這裡做任何事情,可以遍歷整個元素樹並改變其中的任何一個元素屬性。

操縱 state HOC 可以讀取、編輯和刪除被包裹元件例項的 state,也可以按需增加更多的 state。要謹記如果把 state 搞亂會很糟糕。大部分 HOC 應該限制讀取或增加 state,而後者(譯註:增加 state)應該使用名稱空間以免和被包裹元件的 state 搞混。
例子:對訪問被包裹元件的 props 和 state 的除錯 export function IIHOCDEBUGGER(WrappedComponent) {  return class II extends WrappedComponent {    render() {      return (        <div>          <h2>HOC Debugger Component</h2>          <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>          <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>          {super.render()}        </div>      )    }  }} 該 HOC 將被包裹元件嵌入其他元素中,並顯示了其 props 和 state。 使用 HOC 時,就失去了被包裹元件原有的名字,可能會影響開發和除錯。 hoc總結:
  • render 劫持(在繼承反轉中看見過)
  • 控制內部 props(同樣在繼承反轉中看見過)
  • 抽象 state,但存在缺點。將無法在外部訪問父元素的 state,除非特意為止建立鉤子。這限制了其實用性
  • 包裹新的 React Elements。這可能是父元件唯一強於 HOC 的用例,雖然 HOC 也能做到

  • 操縱子元件有一些陷阱。比如說如果 children 的根一級並不只有單一的子元件(多於一個的第一級子元件),你就得新增一個額外的元素來收納所有子元素,這會讓你的程式碼有些冗繁。在 HOC 中一個單一的頂級子元件是被 React/JSX 的約束所保證的。

轉自:https://mp.weixin.qq.com/s/dtlrOGTjoneOIiM5kB3XvQ(部分理解稍做修改)