摘要:博客地址背景知識在開始講述高階組件前,我們先來回顧高階函數(shù)的定義接收函數(shù)作為輸入,或者輸出另一個函數(shù)的一類函數(shù),被稱作高階函數(shù)。
博客地址:http://www.luckyjing.com/post...
背景知識在開始講述高階組件前,我們先來回顧高階函數(shù)的定義:接收函數(shù)作為輸入,或者輸出另一個函數(shù)的一類函數(shù),被稱作高階函數(shù)。對于高階組件,它描述的便是接受React組件作為輸入,輸出一個新的React組件的組件。
更通俗地描述為,高階組件通過包裹(wrapped)被傳入的React組件,經(jīng)過一系列處理,最終返回一個相對增強(enhanced)的React組件,供其他組件調(diào)用。
實現(xiàn)一個高階組件下面我們來實現(xiàn)一個最簡單的高階組件(函數(shù)),它接受一個React組件,包裹后然后返回。
export default function withHeader(WrappedComponent) { return class HOC extends Component { render() { return} } }我是標題
在其他組件里,我們引用這個高階組件,用來強化它。
@withHeader export default class Demo extends Component { render() { return (我是一個普通組件); } }
在這里使用了ES7里的decorator,來提升寫法上的優(yōu)雅,但是實際上它只是一個語法糖,下面這種寫法也是可以的。
const EnhanceDemo = withHeader(Demo);
隨后,觀察React組件樹發(fā)生了什么變化,如圖所示,可以發(fā)現(xiàn)Demo組件被HOC組件包裹起來了,符合了高階組件的預(yù)期,即組件是層層包裹起來的,如同洋蔥一樣。
但是隨之帶來的問題是,如果這個高階組件被使用了多次,那么在調(diào)試的時候,將會看到一大堆HOC,所以這個時候需要做一點小優(yōu)化,就是在高階組件包裹后,應(yīng)當保留其原有名稱。
我們改寫一下上述的高階組件代碼,增加了getDisplayName函數(shù)以及靜態(tài)屬性displayName,此時再去觀察DOM Tree。
function getDisplayName(component) { return component.displayName || component.name || "Component"; } export default function (WrappedComponent) { return class HOC extends Component { static displayName = `HOC(${getDisplayName(WrappedComponent)})` render() { return} } }我是標題
此時,原本組件的名稱正確地顯示在了DOM Tree上。
這個簡單的例子里高階組件只做了一件事,那便是為被包裹的組件添加一個標題樣式。這個高階組件可以用到任何一個需要添加此邏輯的組件上,只需要被此高階組件修飾即可。
由此可以看出,高階組件的主要功能是封裝并抽離組件的通用邏輯,讓此部分邏輯在組件間更好地被復(fù)用。
高階組件的進階用法 組件參數(shù)還是以上述例子為例,此高階組件僅僅只是展示了我是標題這個名稱,但是為了更好的抽象,此標題應(yīng)當可以被參數(shù)化,如下方式調(diào)用。
// 如果傳入?yún)?shù),則傳入的參數(shù)將作為組件的標題呈現(xiàn) @withHeader("Demo") export default class Demo extends Component { render() { return ( //... ); } }
withHeader需要被改寫成如下形式,它接受一個參數(shù),然后返回一個高階組件(函數(shù))。
export default function (title) { return function (WrappedComponent) { return class HOC extends Component { render() { return} } } }{title ? title : "我是標題"}
使用ES6寫法可以更加簡潔。
export default(title) => (WrappedComponent) => class HOC extends Component { render() { return} }{title ? title : "我是標題"}
如圖可以看到,傳入的參數(shù)已經(jīng)反映在DOM Tree里了。
基于屬性代理的方式柯里化 Curry
概念:只傳遞函數(shù)的一部分參數(shù)來調(diào)用它,讓它返回一個函數(shù)去處理剩下的參數(shù)。
函數(shù)簽名:fun(params)(otherParams)
應(yīng)用:在React里,通過柯里化,我們可以通過傳入不同的參數(shù)來得到不同的高階組件。
屬性代理是最常見的高階組件的使用方式,上述描述的高階組件就是這種方式。它通過做一些操作,將被包裹組件的props和新生成的props一起傳遞給此組件,這稱之為屬性代理。
export default function withHeader(WrappedComponent) { return class HOC extends Component { render() { const newProps = { test:"hoc" } // 透傳props,并且傳遞新的newProps return基于反向繼承的方式} } }
這種方式返回的React組件繼承了被傳入的組件,所以它能夠訪問到的區(qū)域、權(quán)限更多,相比屬性代理方式,它更像打入組織內(nèi)部,對其進行修改。具體的可以參考附錄里提供的鏈接進行深入學(xué)習(xí)。
export default function (WrappedComponent) { return class Inheritance extends WrappedComponent { componentDidMount() { // 可以方便地得到state,做一些更深入的修改。 console.log(this.state); } render() { return super.render(); } } }組合多個高階組件
上述高階組件為React組件增強了一個功能,如果需要同時增加多個功能需要怎么做?這種場景非常常見,例如我既需要增加一個組件標題,又需要在此組件未加載完成時顯示Loading。
@withHeader @withLoading class Demo extends Component{ }
使用compose可以簡化上述過程,也能體現(xiàn)函數(shù)式編程的思想。
const enhance = compose(withHeader,withLoading); @enhance class Demo extends Component{ }
高階組件實戰(zhàn) 實現(xiàn)一個loading組件組合 Compose
compose可以幫助我們組合任意個(包括0個)高階函數(shù),例如compose(a,b,c)返回一個新的函數(shù)d,函數(shù)d依然接受一個函數(shù)作為入?yún)ⅲ徊贿^在內(nèi)部會依次調(diào)用c,b,a,從表現(xiàn)層對使用者保持透明。
基于這個特性,我們便可以非常便捷地為某個組件增強或減弱其特征,只需要去變更compose函數(shù)里的參數(shù)個數(shù)便可。
compose函數(shù)實現(xiàn)方式有很多種,這里推薦其中一個recompact.compose,詳情見下方參考類庫。
實現(xiàn)Loading組件時,發(fā)現(xiàn)需要去攔截它的渲染過程,故使用了反向繼承的方式來完成。
在通過裝飾器調(diào)用時,需要傳入一個函數(shù)作為入?yún)ⅲ瘮?shù)可以獲取到props,隨后返回一個Boolean對象,來決定組件是否需要顯示Loading態(tài)
import React, {Component} from "react"; import {Spin} from "antd"; export default function (loadingCheck) { return function (WrappedComponent) { return class extends WrappedComponent { componentWillUpdate(nextProps, nextState) { console.log("withLoading將會更新"); } render() { if (loadingCheck(this.props)) { return實現(xiàn)一個copy組件{super.render()} } else { return super.render(); } } } } } // 使用 @withLoading(props => { return props.IndexStore.accountList.length == 0; })
實現(xiàn)copy組件的時候,我們發(fā)現(xiàn)不需要去改變組件內(nèi)部的展示方式,只是為其在外圍增加一個功能,并不會侵入被傳入的組件,故使用了屬性代理的方式。
import gotem from "gotem"; import React, {Component} from "react"; import ReactDom from "react-dom"; import {message} from "antd"; export default copy = (targetName) => { return (WrappedComponent) => { return class extends Component { componentDidMount() { const ctx = this; const dom = ReactDom.findDOMNode(ctx); const nodes = { trigger: dom, // targetName為DOM選擇器,復(fù)制組件將會復(fù)制它的值 target: dom.querySelector(targetName) }; gotem(nodes.trigger, nodes.target, { success: function () { message.success("復(fù)制成功"); }, error: function () { message.error("復(fù)制失敗,請手動輸入"); } }); } render() { return與父組件區(qū)別; } }; }; } // 使用 // 傳入 h3 ,讓復(fù)制組件去獲取它的值 @copy("h3") class Info extends Component { render() { return ( ); } }阿里云,點擊復(fù)制這段文字
高階組件作為一個函數(shù),它可以更加純粹地關(guān)注業(yè)務(wù)邏輯層面的代碼,比如數(shù)據(jù)處理,數(shù)據(jù)校驗,發(fā)送請求等,可以改善目前代碼里業(yè)務(wù)邏輯和UI邏輯混雜在一起的現(xiàn)狀。父組件則是UI層的東西,我們先前經(jīng)常把一些業(yè)務(wù)邏輯處理放在父組件里,這樣會造成父組件混亂的情況。為了代碼進一步解耦,可以考慮使用高階組件這種模式。
開源的高階組件使用賞析 recompactrecompact提供了一系列使用的高階組件,可以增強組件的行為,可以利用此庫學(xué)習(xí)高階組件的寫法。
import recompact from "recompact" import { pure, withProps } from "recompact" const enhance = recompact.compose( withProps({ className: "beautiful" }), pure, ) @enhance class Demo extends Component{ }React Sortable
通過使用此庫提供的高階組件,可以方便地讓列表元素可拖動。
總結(jié)高階組件是Decorator模式在React的一種實現(xiàn),它可以抽離公共邏輯,像洋蔥一樣層層疊加給組件,每一層職能分明,可以方便地抽離與增添。在優(yōu)化代碼或解耦組件時,可以考慮使用高階組件模式。
參考recompact:包含了一系列實用的高階組件庫
React Sortable:React拖動庫
深入理解 React 高階組件:其中詳細介紹了屬性代理和反向繼承的區(qū)別。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/84368.html
摘要:在項目中用好高階組件,可以顯著提高代碼質(zhì)量。高階組件的定義類比于高階函數(shù)的定義。高階函數(shù)接收函數(shù)作為參數(shù),并且返回值也是一個函數(shù)。 React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深對React的理解,以及在項目中更加靈活地使用React。 1. 基本概念 高階組件是React 中一個很重要且比較復(fù)雜的概念,高階組件在很多第三方庫(如Redux)中都...
摘要:本篇是深入系列的最后一篇,將介紹開發(fā)應(yīng)用時,經(jīng)常用到的模式,這些模式并非都有官方名稱,所以有些模式的命名并不一定準確,請讀者主要關(guān)注模式的內(nèi)容。 React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深對React的理解,以及在項目中更加靈活地使用React。 本篇是React深入系列的最后一篇,將介紹開發(fā)React應(yīng)用時,經(jīng)常用到的模式,這些模式并非都有...
摘要:與繼承相比,裝飾者是一種更輕便靈活的做法。它只是一種模式,這種模式是由自身的組合性質(zhì)必然產(chǎn)生的。對比原生組件增強的項可操作所有傳入的可操作組件的生命周期可操作組件的方法獲取反向繼承返回一個組件,繼承原組件,在中調(diào)用原組件的。 導(dǎo)讀 前端發(fā)展速度非常之快,頁面和組件變得越來越復(fù)雜,如何更好的實現(xiàn)狀態(tài)邏輯復(fù)用一直都是應(yīng)用程序中重要的一部分,這直接關(guān)系著應(yīng)用程序的質(zhì)量以及維護的難易程度。 本...
閱讀 781·2023-04-25 16:55
閱讀 2818·2021-10-11 10:59
閱讀 2081·2021-09-09 11:38
閱讀 1795·2021-09-03 10:40
閱讀 1493·2019-08-30 15:52
閱讀 1133·2019-08-30 15:52
閱讀 964·2019-08-29 15:33
閱讀 3505·2019-08-29 11:26