目錄
一、基本概念
二、使用場景
1.操縱props
2.透過 ref 存取元件實例
3.元件狀態提升
三、參數傳遞
四、繼承方式實作高階元件
首頁 web前端 js教程 React高階元件是什麼? React高階元件的詳細講解

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

Sep 14, 2018 pm 01:55 PM
react.js

本篇文章帶給大家的內容是關於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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

React父元件怎麼呼叫子元件的方法 React父元件怎麼呼叫子元件的方法 Dec 27, 2022 pm 07:01 PM

呼叫方法:1、類別元件中的呼叫可以利用React.createRef()、ref的函數式宣告或props自訂onRef屬性來實作;2、函式元件、Hook元件中的呼叫可以利用useImperativeHandle或forwardRef拋出子組件ref來實作。

深入理解React的自訂Hook 深入理解React的自訂Hook Apr 20, 2023 pm 06:22 PM

React 自訂 Hook 是將元件邏輯封裝在可重複使用函數中的方式,它們提供了一種在不編寫類別的情況下重複使用狀態邏輯的方式。本文將詳細介紹如何自訂封裝 hook。

怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 Mar 31, 2023 pm 06:54 PM

怎麼調試R​​eact源碼?以下這篇文章帶大家聊聊多種工具下的調試React源碼的方法,介紹一下在貢獻者、create-react-app、vite專案中如何debugger React的真實源碼,希望對大家有所幫助!

React為什麼不將Vite作為構建應用的首選 React為什麼不將Vite作為構建應用的首選 Feb 03, 2023 pm 06:41 PM

React為什麼不將Vite作為建置應用的首選?以下這篇文章就來帶大家聊聊React不將Vite當作預設推薦的原因,希望對大家有幫助!

react怎麼設定div高度 react怎麼設定div高度 Jan 06, 2023 am 10:19 AM

react設定div高度的方法:1、透過css方式實現div高度;2、在state中宣告一個物件C,並在該物件中存放更換按鈕的樣式,然後取得A並重新設定C中的「marginTop」即可。

7 個很棒且實用的React 元件庫(壓箱底分享) 7 個很棒且實用的React 元件庫(壓箱底分享) Nov 04, 2022 pm 08:00 PM

這篇文章跟大家整理分享7 個很棒又實用的React 元件庫,日常開發中常會用到的,快來收藏試試看吧!

10 個編寫更簡潔React程式碼的實用小技巧 10 個編寫更簡潔React程式碼的實用小技巧 Jan 03, 2023 pm 08:18 PM

這篇文章為大家整理分享 10 個寫更簡潔 React 程式碼的實用小技巧,希望對大家有幫助!

聊聊Vuex與Pinia在設計與實作上的差別 聊聊Vuex與Pinia在設計與實作上的差別 Dec 07, 2022 pm 06:24 PM

在進行前端專案開發時,狀態管理始終是一個繞不開的話題,Vue 與 React 框架本身提供了一部分能力去解決這個問題。但在開發大型應用程式時往往有其他考慮,例如需要更規範更完善的操作日誌、整合在開發者工具中的時間旅行能力、服務端渲染等。本文以 Vue 架構為例,介紹 Vuex 與 Pinia 這兩種狀態管理工具在設計與實作上的差異。

See all articles