搜尋
首頁web前端js教程探索在 React 中引入高階元件的最佳實踐

探索在 React 中引入高阶组件的最佳实践

這是高階元件系列的第三部分。在第一個教程中,我們從零開始。我們學習了 ES6 語法、高階函數和高階組件的基礎知識。

高階元件模式對於建立抽像元件非常有用 - 您可以使用它們與現有元件共用資料(狀態和行為)。在本系列的第二部分中,我示範了使用此模式的程式碼的實際範例。這包括受保護的路由、建立可配置的通用容器、將載入指示器附加到元件等。

在本教程中,我們將了解您在編寫 HOC 時應該考慮的一些最佳實踐和注意事項。

簡介

React 之前有一個名為 Mixins 的東西,它與 React.createClass 方法配合得很好。 Mixins 允許開發人員在組件之間共用程式碼。然而,它們有一些缺點,這個想法最終被放棄了。 Mixin 沒有升級為支援 ES6 類,Dan Abramov 甚至寫了一篇深入的文章來解釋為什麼 Mixin 被認為是有害的。

高階組件作為 Mixins 的替代品出現,並且它們支援 ES6 類別。此外,HOC 不需要對 React API 做任何事情,並且是與 React 配合良好的通用模式。然而,HOC 也有缺陷。儘管高階組件的缺點在較小的項目中可能並不明顯,但您可以將多個高階組件連結到單一組件,如下所示。

const SomeNewComponent = 
        withRouter(RequireAuth(LoaderDemo(GenericContainer(CustomForm(Form)))))

您不應該讓連結發展到您問自己這樣的問題:「這些道具從哪裡來?」本教學解決了高階組件模式的一些常見問題以及正確解決這些問題的解決方案。

HOC 的問題

與 HOC 相關的一些常見問題與 HOC 本身關係不大,而與它們的實現有關。

如您所知,HOC 非常適合程式碼抽象化和建立可重複使用程式碼。然而,當你堆疊了多個 HOC 時,如果某些東西看起來不合適或某些 props 沒有顯示,那麼調試起來就會很痛苦,因為 React DevTools 為你提供的有關可能出現問題的線索非常有限。

現實世界的 HOC 問題

為了了解 HOC 的缺點,我創建了一個範例演示,其中嵌套了我們在上一教程中創建的一些 HOC。我們有四個高階函數包裝單一 ContactList 元件。如果程式碼沒有意義或您沒有遵循我之前的教程,這裡是其工作原理的簡短摘要。

withRouter 是一個 HOC,是 React-router 套件的一部分。它使您可以存取歷史物件的屬性,然後將它們作為道具傳遞。

#withAuth 尋找 authentication 屬性,如果驗證為 true,則呈現 WrappedComponent。如果驗證為 false,則會將 '/login' 推送到歷史物件。

withGenericContainer 除了 WrappedComponent 之外還接受一個物件作為輸入。 GenericContainer 進行 API 呼叫並將結果儲存在狀態中,然後將資料作為 props 傳送到包裝的元件。

withLoader 是一個附加載入指標的 HOC。指示器旋轉,直到所獲得的數據達到狀態。

BestPracticeDemo.jsx

##
class BestPracticesDemo extends Component {

    render() {

		return(
            <div className="contactApp">
    			<ExtendedContactList authenticated = {true} {...this.props} contacts ="this" />
    	    </div>
     	)
	}
}

const ContactList = ({contacts}) => {
	
	return(
		<div>
			<ul>
      {contacts.map(
        (contact) => <li key={contact.email}>
         
          <img src={contact.photo}    style="max-width:90%" height="100px"  alt="探索在 React 中引入高階元件的最佳實踐" />
          <div className="contactData">
          <h4 id="contact-name">{contact.name}</h4>
           <small>{contact.email}</small>  <br/><small> {contact.phone}</small>
          </div>
         
        </li>
      )}
    </ul>
		</div>
		)
}

const reqAPI = {reqUrl: 'https://demo1443058.mockable.io/users/', 
                reqMethod:'GET', resName:'contacts'}	

const ExtendedContactList = withRouter(
                                withAuth(
                                    withGenericContainer(reqAPI)(
                                        withLoader('contacts')
                                            (ContactList))));

export default BestPracticesDemo;

現在您可以親眼看到高階組件的一些常見陷阱。讓我們詳細討論其中的一些內容。

基本注意事項

不要忘記在 HOC 中傳播道具 假設我們在組合層次結構的頂端有一個 authenticated = { this.state.authenticated } 屬性。我們知道這是一個重要的道具,並且應該將其延伸到演示組件。然而,想像一下中間 HOC,例如

withGenericContainer

,決定忽略它的所有 props。

//render method of withGenericContainer
render() {
	return(
		<WrappedComponent />
    )
}

這是一個非常常見的錯誤,在編寫高階元件時應該盡量避免。不熟悉 HOC 的人可能會發現很難弄清楚為什麼所有的 props 都遺失了,因為很難隔離問題。所以,永遠記得在你的 HOC 中傳播 props。

//The right way

render() {
	return(
		<WrappedComponent {...this.props} {...this.state} />)
}

不要傳遞超出 HOC 範圍的不存在的 props

HOC 可能會引入 WrappedComponent 可能沒有任何用處的新屬性。在這種情況下,傳遞僅與組合元件相關的 props 是一個很好的做法。 高階元件可以透過兩種方式接受資料:作為函數的參數或作為組件的 prop。例如, authenticated = { this.state.authenticated } 是一個 prop 範例,而在

withGenericContainer(reqAPI)(ContactList)### 中,我們將資料傳遞為參數。 ###

因为 withGenericContainer 是一个函数,所以您可以根据需要传入任意数量的参数。在上面的示例中,配置对象用于指定组件的数据依赖性。然而,增强组件和包装组件之间的契约是严格通过 props 进行的。

因此,我建议通过函数参数填充静态时间数据依赖项,并将动态数据作为 props 传递。经过身份验证的道具是动态的,因为用户可以通过身份验证,也可以不通过身份验证,具体取决于他们是否登录,但我们可以确定 reqAPI 对象的内容不会动态更改。

不要在渲染方法中使用 HOC

这是一个您应该不惜一切代价避免的示例。

var OriginalComponent = () => <p>Hello world.</p>;

class App extends React.Component {
  render() {
    return React.createElement(enhanceComponent(OriginalComponent));
  }
};

除了性能问题之外,您还将在每次渲染时丢失 OriginalComponent 及其所有子组件的状态。要解决这个问题,请将 HOC 声明移到 render 方法之外,使其仅创建一次,以便渲染始终返回相同的EnhancedComponent。

var OriginalComponent = () => <p>Hello world.</p>;
var EnhancedComponent = enhanceComponent(OriginalComponent);

class App extends React.Component {
  render() {
    return React.createElement(EnhancedComponent);
  }
};

不要改变包装组件

改变 HOC 内的包装组件将导致无法在 HOC 外部使用包装组件。如果您的 HOC 返回 WrappedComponent,您几乎总是可以确定自己做错了。下面的例子演示了突变和组合之间的区别。

function logger(WrappedComponent) {
 WrappedComponent.prototype.componentWillReceiveProps = function(nextProps) {
    console.log('Current props: ', this.props);
    console.log('Next props: ', nextProps);
  };
  // We're returning the WrappedComponent rather than composing
  //it
  return WrappedComponent;
}

组合是 React 的基本特征之一。您可以在其渲染函数中将一个组件包装在另一个组件内,这就是所谓的组合。

function logger(WrappedComponent) {
  return class extends Component {
    componentWillReceiveProps(nextProps) {
      console.log('Current props: ', this.props);
      console.log('Next props: ', nextProps);
    }
    render() {
      // Wraps the input component in a container, without mutating it. Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}

此外,如果您改变 HOC 内的 WrappedComponent,然后使用另一个 HOC 包装增强组件,则第一个 HOC 所做的更改将被覆盖。为了避免这种情况,您应该坚持组合组件而不是改变它们。

命名空间通用 Propnames

当您有多个堆叠时,命名空间道具名称的重要性是显而易见的。组件可能会将 prop 名称推送到已被另一个高阶组件使用的 WrappedComponent 中。

import React, { Component } from 'react';

const withMouse = (WrappedComponent) => {
  return class withMouse extends Component {
    constructor(props) {
      super(props);
      this.state = {
        name: 'Mouse'
      }
    }

    render() {

      return(
        <WrappedComponent {...this.props}  name={this.state.name} />
      );
    
    }
  }
}


const withCat = (WrappedComponent) => {
  return class withCat extends Component {

    render() {
      return(
        <WrappedComponent {...this.props} name= "Cat"  /> 
      )
    }
  }
}

const NameComponent = ({name}) => {
  
  return(
    <div> {name} </div>)
}


const App =() => {

  const EnhancedComponent  = withMouse(withCat(NameComponent));
  
  return(
  <div> <EnhancedComponent />  </div>)
}

export default App;

withMousewithCat 都在尝试推送自己的 name 版本。如果EnhancedComponent也必须共享一些同名的props怎么办?

<EnhancedComponent name="This is important" />

这不会给最终开发人员带来混乱和误导吗? React Devtools 不会报告任何名称冲突,您必须查看 HOC 实现细节才能了解出了什么问题。

这可以通过提供 HOC 属性名称的范围作为约定来解决。因此,您将拥有 withCat_namewithMouse_name 而不是通用的 prop 名称。

这里需要注意的另一件有趣的事情是,对属性进行排序在 React 中非常重要。当您多次拥有相同的属性并导致名称冲突时,最后一个声明将始终保留。在上面的例子中,Cat 获胜,因为它被放置在 { ...this.props } 之后。

如果您希望通过其他方式解决名称冲突,您可以重新排序属性并在最后传播 this.props 。这样,您就可以设置适合您的项目的合理默认值。

使用有意义的显示名称使调试更容易

由 HOC 创建的组件在 React Devtools 中显示为普通组件。很难区分两者。您可以通过为高阶组件提供有意义的 displayName 来简化调试。在 React Devtools 上拥有这样的东西不是明智的吗?

<withMouse(withCat(NameComponent)) > 
... 
</withMouse(withCat(NameComponent))>

那么 displayName 是什么?每个组件都有一个 displayName 属性,可用于调试目的。最流行的技术是包装 WrappedComponent 的显示名称。如果 withCat 是 HOC,并且 NameComponentWrappedComponent,则 displayName 将是 withCat(NameComponent).

const withMouse = (WrappedComponent) => {
  class withMouse extends Component {
    /*                       */   
 }

  withMouse.displayName = `withMouse(${getDisplayName(WrappedComponent)})`;
  return withMouse;
}

const withCat = (WrappedComponent) => {
  class withCat extends Component {
   /*                          */
  }

  withCat.displayName = `withCat(${getDisplayName(WrappedComponent)})`;
  return withCat;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

高阶组件的替代方案

尽管 Mixins 已经消失,但说高阶组件是唯一允许代码共享和抽象的模式是有误导性的。另一种替代模式已经出现,我听说有人说它比 HOC 更好。深入探讨这个概念超出了本教程的范围,但我将向您介绍渲染道具和一些基本示例,以演示它们为何有用。

渲染道具有许多不同的名称:

  • 渲染道具
  • 儿童道具
  • 像孩子一样发挥作用
  • 渲染回调

这是一个简单的示例,应该解释渲染道具的工作原理。

class Mouse extends Component {

  constructor() {
    super();
    this.state = {
      name: "Nibbles"
    }
  }
  render() {
    return(
      <div>
        {this.props.children(this.state)}
      </div>
    )
  
  }
}

class App extends Component {
  render() {
    return(
      <Mouse>
        {(mouse) => <div> The name of the mouse is {mouse.name} </div> }
      </Mouse> 
      )
  }
}

如您所见,我们已经摆脱了高阶函数。我们有一个名为 Mouse 的常规组件。我们将渲染 this.props.children() 并将状态作为参数传递,而不是在其 render 方法中渲染包装的组件。所以我们给 Mouse 一个 render prop,而 render prop 决定应该渲染什么。

換句話說,Mouse 元件接受一個函數作為子屬性的值。當 Mouse 渲染時,它會傳回 Mouse 的狀態,而 render prop 函數可以隨意使用它。

我喜歡這種模式的幾點:

  • 從可讀性的角度來看,道具的來源更加明顯。
  • 這種模式是動態且靈活的。 HOC 是在靜態時間組成的。儘管我從未發現這是一個限制,但渲染道具是動態組合的並且更加靈活。
  • 簡化的元件組成。您可以告別嵌套多個 HOC。

結論

高階元件是可用於在 React 中建立健全、可重複使用元件的模式。如果您要使用 HOC,則應遵循一些基本規則。這是為了讓您以後不會後悔使用它們的決定。我在本教程中總結了大部分最佳實踐。

HOC 並不是當今唯一流行的模式。在本教學的最後,我向您介紹了另一種稱為渲染道具的模式,該模式在 React 開發人員中越來越受歡迎。

我不會判斷一種模式並說這個模式比另一個模式更好。隨著 React 的發展以及它周圍的生態系統的成熟,越來越多的模式將會出現。在我看來,您應該學習所有這些,並堅持選擇適合您的風格並且您感到舒適的一個。

這也標誌著高階元件教學系列的結束。我們已經從零開始,掌握了一種稱為 HOC 的先進技術。如果我錯過了任何內容或您有建議/想法,我很樂意聽到。您可以將它們發佈在評論中。

以上是探索在 React 中引入高階元件的最佳實踐的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

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