首頁  >  文章  >  web前端  >  React高階元件是什麼? React高階元件的詳細講解

React高階元件是什麼? React高階元件的詳細講解

不言
不言原創
2018-09-14 13:55:302252瀏覽

本篇文章帶給大家的內容是關於React高階元件是什麼? React高階元件的詳細講解,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

一、基本概念

高階函數是以函數為參數,並且傳回也是函數的的函數。類似的,高階元件(簡稱HOC)接收 React 元件為參數,並且傳回一個新的React元件。高階組件本質也是函數,並不是組件。高階組件的函數形式如下:

const EnhanceComponent = higherOrderComponent(WrappedComponent)

透過一個簡單的例子解釋高階元件是如何重複使用的。現在有一個元件MyComponent,需要從LocalStorage取得數據,然後渲染到介面。一般情況下,我們可以這樣實作:

import React, { Component } from 'react'

class MyComponent extends Component {
  componentWillMount() {
    let data = localStorage.getItem('data');
    this.setState({data});
  }
  render() {
    return(
      <div>{this.state.data}</div>
    )
  }
}

程式碼很簡單,但當其它元件也需要從LocalStorage 取得相同的資料展示出來時,每個元件都需要重寫一次componentWillMount 中的程式碼,這顯然是很冗餘的。下面讓我人來看看使用高階元件改寫這部分程式碼。

import React, { Component } from 'react'

function withPersistentData(WrappedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem('data');
      this.setState({data});
    }
    render() {
      // 通过{ ...this.props} 把传递给当前组件属性继续传递给被包装的组件
      return <WrappedComponent data={this.state.data} {...this.props}/>
    }
  }
}

class MyComponent extends Component{
  render() {
    return <p>{this.props.data}</p>
  }
}

const MyComponentWithPersistentData = withPersistentData(MyComponent);

withPersistentData 就是一個高階元件,它傳回一個新的元件,在新元件中componentWillMount 中統一處理從LocalStorage 取得資料邏輯,然後將取得到的資料透過props 傳遞給被包裝的元件WrappedComponent,這樣在WrappedComponent中就可以直接使用this.props.data 取得需要展示的數據,當有其他的元件也需要這段邏輯時,繼續使用withPersistentData 這個高階元件包裝這些元件。

二、使用場景

高階元件的使用場景主要有以下4:
1)操縱props
2) 透過ref 存取元件實例
3)元件狀態提升
4)用其他元素包裝元件

1.操縱props

在被包裝元件接收props 前,高階元件可以先攔截到props, 對props 執行增加、刪除或修改的操作,然後將處理後的props 再傳遞被包裝組件,一中的例子就是屬於這種情況。

2.透過 ref 存取元件實例

高階元件 ref 取得被包裝元件實例的引用,然後高階元件就具備了直接運算被包裝元件的屬性或方法的能力。

import React, { Component } from 'react'

function withRef(wrappedComponent) {
  return class extends Component{
    constructor(props) {
      super(props);
      this.someMethod = this.someMethod.bind(this);
    }

    someMethod() {
      this.wrappedInstance.comeMethodInWrappedComponent();
    }

    render() {
      // 为被包装组件添加 ref 属性,从而获取组件实例并赋值给 this.wrappedInstance
      return <wrappedComponent ref={(instance) => { this.wrappedInstance = instance }} {...this.props}/>
    }
  }
}

當 wrappedComponent 被渲染時,執行 ref 的回呼函數,高階元件透過 this.wrappedInstance 儲存 wrappedComponent 實例引用,在 someMethod 中透過 this.wrappedInstance 呼叫 wrappedComponent 中的方法。這種用法在實際專案中很少會被用到,但當高階組件封裝的複用邏輯需要被包裝組件的方法或屬性的協同支持時,這種用法就有了用武之地。

3.元件狀態提升

高階元件可以透過將被包裝組件的狀態及對應的狀態處理方法提升到高階元件本身內部實現被包裝元件的無狀態化。一個典型的場景是,利用高階元件將原本受控元件需要自行維護的狀態統一提升到高階元件中。

import React, { Component } from 'react'

function withRef(wrappedComponent) {
  return class extends Component{
    constructor(props) {
      super(props);
      this.state = {
        value: ''
      }
      this.handleValueChange = this.handleValueChange.bind(this);
    }

    handleValueChange(event) {
      this.this.setState({
        value: event.EventTarget.value
      })
    }

    render() {
      // newProps保存受控组件需要使用的属性和事件处理函数
      const newProps = {
        controlledProps: {
          value: this.state.value,
          onChange: this.handleValueChange
        }
      }
      return <wrappedComponent {...this.props} {...newProps}/>
    }
  }
}

這個例子把受控元件value 屬性用到的狀態和處理value 變化的回呼函數都提升到高階元件中,當我們再使用受控元件時,就可以這樣使用:

import React, { Component } from 'react'

function withControlledState(wrappedComponent) {
  return class extends Component{
    constructor(props) {
      super(props);
      this.state = {
        value: ''
      }
      this.handleValueChange = this.handleValueChange.bind(this);
    }

    handleValueChange(event) {
      this.this.setState({
        value: event.EventTarget.value
      })
    }

    render() {
      // newProps保存受控组件需要使用的属性和事件处理函数
      const newProps = {
        controlledProps: {
          value: this.state.value,
          onChange: this.handleValueChange
        }
      }
      return <wrappedComponent {...this.props} {...newProps}/>
    }
  }
}


class  SimpleControlledComponent extends React.Component {
  render() {
    // 此时的 SimpleControlledComponent 为无状态组件,状态由高阶组件维护
    return <input name="simple" {...this.props.controlledProps}/>
  }
}

const ComponentWithControlledState = withControlledState(SimpleControlledComponent);

三、參數傳遞

高階元件的參數並非只能是一個元件,它還可以接收其他參數。例如一中是從 LocalStorage 取得 key 為 data的數據,當需要取得資料的 key不確定時,withPersistentData 這個高階元件就不滿足需求了。我們可以讓它接收一個額外參數來決定從LocalStorage 中取得哪個資料:

import React, { Component } from 'react'

function withPersistentData(WrappedComponent, key) {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
      this.setState({ data });
    }
    render() {
      // 通过{ ...this.props} 把传递给当前组件属性继续传递给被包装的组件
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}

class MyComponent extends Component {
  render() {
    return <p>{this.props.data}</p>
  }
}
// 获取 key='data' 的数据
const MyComponent1WithPersistentData = withPersistentData(MyComponent, 'data');

// 获取 key='name' 的数据
const MyComponent2WithPersistentData = withPersistentData(MyComponent, 'name');

新版本的withPersistentData 滿足取得不同key 值的需求,但實際情況中,我們很少使用這種方式傳遞參數,而是採用更靈活、更具能用性的函數形式:

HOC(...params)(WrappedComponent)

HOC(...params) 的返回值是一個高階元件,高階元件需要的參數是先傳遞HOC 函數的。用這種形式改寫withPersistentData 如下(注意:這種形式的高階元件使用箭頭函數定義更為簡潔):

import React, { Component } from 'react'

const withPersistentData = (key) => (WrappedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStorage.getItem(key);
      this.setState({ data });
    }
    render() {
      // 通过{ ...this.props} 把传递给当前组件属性继续传递给被包装的组件
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}

class MyComponent extends Component {
  render() {
    return <p>{this.props.data}</p>
  }
}
// 获取 key='data' 的数据
const MyComponent1WithPersistentData = withPersistentData('data')(MyComponent);

// 获取 key='name' 的数据
const MyComponent2WithPersistentData = withPersistentData('name')(MyComponent);

四、繼承方式實作高階元件

前面介紹的高階元件的實作方式都是由高階元件處理通用邏輯,然後將相關屬性傳遞給被包裝元件,我們稱這種方式為屬性代理。除了屬性代理外,還可以透過繼承方式實現高階元件:透過 繼承被包裝元件實現邏輯的複用。繼承方式實現的高階元件常用於渲染劫持。例如,當使用者處於登入狀態時,允許元件渲染,否則渲染一個空元件。程式碼如下:

function withAuth(WrappedComponent) {
  return class extends WrappedComponent {
    render() {
      if (this.props.loggedIn) {
        return super.render();
      } else {
        return null;
      }
    }
  }
}

根據WrappedComponent的 this.props.loggedIn 判讀使用者是否已登錄,如果登錄,就透過 super.render()呼叫WrappedComponent 的render 方法正常渲染元件,否則傳回一個null,繼承方式實現高階元件對被包裝元件具有侵入性,當組合多個高階使用時,很容易因為子類別元件忘記透過super呼叫父類別元件方法而導致邏輯遺失。因此,在使用高階元件時,應盡量透過代理方式實現高階元件。

相關推薦:

超級給力的JavaScript的React框架入門教學_基礎知識

#MySQL高可用元件MHA參數詳解

##############################################################

以上是React高階元件是什麼? React高階元件的詳細講解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn