高階組件(hoc)是 react 中一項有趣的技術,用于重構共享幾乎相同邏輯的類似組件。我知道這聽起來很抽象而且很高級。然而,它是一種并非特定于 react 的架構模式,因此您可以使用該方法來做很多事情。
例如,您可以使用它向某個組件添加加載指示器,而無需調整原始組件,或者您可以隱藏組件的屬性以使其不那么冗長。應用程序有很多,我試圖在本教程中介紹其中的大部分。
還有其他幾個教程可以教您有關 HOC 的知識,但其中大多數都是針對高級 React 開發人員的。當我開始學習 React 時,我很難理解高階組件的概念以及如何將 HOC 合并到我的項目中以編寫更好的代碼。本文將解釋您需要了解的 HOC 從頭到孵化的所有內容。
概述
本教程分為三個部分。第一部分將介紹高階組件的概念。在這里,我們將討論在查看高階函數和 HOC 之前需要了解的語法。第二部分是本系列中最令人興奮的部分,您將看到 HOC 的實際示例。我們將使用 HOC 來創建表單、授權和許多其他事情。
在本教程的第三部分中,我們將更多地關注最佳實踐以及實現高階組件時需要考慮的事項。我們還將簡要介紹 React 中代碼共享的替代模式,例如 Render props。
在開始之前,最好先看看有狀態組件與無狀態組件的教程,以便更好地理解 React 的組件架構。
ES6 語法備忘單
我們很快就會動手。但在此之前,我認為您應該了解一些事情。我更喜歡盡可能使用 ES6 語法,它與 HOC 配合得很好。作為初學者,HOC 有意義,但某些 ES6 語法卻沒有意義。因此,我建議您先瀏覽一遍本節,稍后您可以再回來參考。
箭頭函數
箭頭函數是常規函數表達式,但語法較短。它們最適合非方法函數,這也是我們特別感興趣的。以下是一些幫助您入門的示例:
不帶參數的函數
/* Functions without parameters */ function () { return "This is a function expression"; } // is equivalent to () => { return "This is an arrow function expression" } // or () => "Arrow with a shorter syntax"
具有單個參數的函數
/* Function with a single parameter */ function (param) { return { title: "This function accepts a parameter and returns an object", params: param} } // is syntax-equivalent to param => { return { title: "This arrow function accepts a single parameter", params: param } }
具有多個參數的函數
/* Function with multiple parameters */ function (param1, param2) { return { title: "This function accepts multiple parameters", params: [param1,param2]} } // is syntax-equivalent to (param1, param2) => { return {title: "Arrow function with multiple parameters", params: [param1, param2] } } // or (param1, param2) => ({ title: "Arrow function with multiple parameters", params: [param1, param2] })
函數式編程中的柯里化
雖然這個名字暗示它與流行的印度美食中的一道異國菜肴有關,但事實并非如此。柯里化可幫助您將接受多個參數的函數分解為一系列一次接受一個參數的函數。這是一個例子:
//Usual sum function const sum = (a, b) => a + b //Curried sum function const curriedSum = function (a) { return function (b) { return a+b } //Curried sum function using arrow syntax const curriedSum = a => b => a+b curriedSum(5)(4) //9
該函數只接受一個參數,并返回一個接受另一個參數的函數,這種情況一直持續到滿足所有參數為止。
curriedSum // (a) => (b) => a+b curriedSum(4) // (b) => 4+b curriedSum(4)(5) //4+5
一個密切相關的術語稱為“部分應用”。部分應用程序通過預先填充現有函數的一些參數來創建新函數。新創建的函數的元數(即參數數量)將小于原始函數的元數。
傳播語法
擴展運算符擴展數組、字符串或對象表達式的內容。以下是您可以使用傳播運算符執行的操作的列表
函數調用中的擴展語法
/*Spread Syntax in Function Calls */ const add = (x,y,z) => x+y+z const args = [1,2,3] add(...args) // 6
數組文字中的擴展語法
/* Spread in Array Literals */ const twoAndThree = ['two', 'three']; const numbers = ['one', ...twoAndThree, 'four', 'five']; // ["one", "two", "three", "four", "five"]
對象文字中的擴展語法
/* Spread in Object Literals */ const contactName = { name: { first: "Foo", middle: "Lux", last: "Bar" } } const contactData = { email: "fooluxbar@example.com", phone: "1234567890" } const contact = {...contactName, ...contactData} /* { name: { first: "Foo", middle: "Lux", last: "Bar" } email: "fooluxbar@example.com" phone: "1234567890" } */
我個人喜歡三個點可以讓您更輕松地將現有道具傳遞給子組件或創建新道具的方式。
React 中的擴展運算符
const ParentComponent = (props) => { const newProps = { foo: 'default' }; return ( <childcomponent></childcomponent> ) }
現在我們已經了解了構建 HOC 的基本 ES6 語法,讓我們看看它們是什么。
高階函數
什么是高階函數?維基百科有一個簡單的定義:
在數學和計算機科學中,高階函數(也稱為泛函、函數形式或函子)是一種接受一個或多個函數作為參數或返回一個函數作為其結果或兩者兼而有之的函數。
您之前可能以某種形式在 JavaScript 中使用過高階函數,因為這就是 JavaScript 的工作方式。將匿名函數或回調作為參數傳遞或返回另一個函數的函數 – 所有這些都屬于高階函數。下面的代碼創建了一個本質上更高階的計算器函數。
const calculator = (inputFunction) => (...args) => { const resultValue = inputFunction(...args); console.log(resultValue); return resultValue; } const add = (...all) => { return all.reduce( (a,b) => a+b,0) ; } const multiply = (...all) => { return all.reduce((a,b)=> a*b,1); }
讓我們更深入地了解一下這一點。 calculator() 接受一個函數作為輸入并返回另一個函數——這完全符合我們對高階函數的定義。因為我們使用了剩余參數語法,所以返回的函數將其所有參數收集在一個數組中。
然后,使用傳遞的所有參數調用輸入函數,并將輸出記錄到控制臺。所以計算器是一個柯里化的高階函數,你可以像這樣使用計算器:
calculator(multiply)(2,4); // returns 8 calculator(add)(3,6,9,12,15,18); // returns 63
插入一個函數,例如 add() 或 multiply() 和任意數量的參數,以及 calculator()將從那里拿走它。所以計算器是一個擴展了 add() 和 multiply() 功能的容器。它使我們能夠在更高或更抽象的層面上處理問題。乍一看,這種方法的好處包括:
- 代碼可以在多個函數中重復使用。
- 您可以在容器級別添加所有算術運算通用的額外功能。
- 更具可讀性,并且能更好地表達程序員的意圖。
現在我們對高階函數有了一個很好的了解,讓我們看看高階組件的能力。
高階組件
高階組件是一個接受組件作為參數并返回該組件的擴展版本的函數。
(InputComponent) => { return ExtendedComponent } // or alternatively InputComponent => ExtendedComponent
擴展組件 組成 InputComponent。 ExtendedComponent 就像一個容器。它呈現 InputComponent,但因為我們返回一個新組件,所以它添加了一個額外的抽象層。您可以使用此層添加狀態、行為甚至樣式。如果您愿意,您甚至可以決定根本不渲染 InputComponent — HOC 能夠做到這一點以及更多。
下面的圖片應該可以消除混亂(如果有的話)。
理論已經講完了,讓我們開始看代碼。下面是一個非常簡單的 HOC 示例,它將輸入組件包裝在
/* The `with` prefix for the function name is a naming convention. You can name your function anything you want as long as it's meaningful */ const withGreyBg = WrappedComponent => class NewComponent extends Component { const bgStyle = { backgroundColor: 'grey', }; render() { return ( <div classname="wrapper" style="{bgStyle}"> <wrappedcomponent></wrappedcomponent> </div> ); } }; const SmallCardWithGreyBg = withGreyBg(SmallCard); const BigCardWithGreyBg = withGreyBg(BigCard); const HugeCardWithGreyBg = withGreyBg(HugeCard); class CardsDemo extends Component { render() { <smallcardwithgreybg></smallcardwithgreybg><bigcardwithgreybg></bigcardwithgreybg><hugecardwithgreybg></hugecardwithgreybg> } }
withGreyBg 函數將一個組件作為輸入并返回一個新組件。我們不是直接組合 Card 組件并將樣式標簽附加到每個單獨的組件,而是創建一個 HOC 來實現此目的。高階組件包裝原始組件并在其周圍添加
雖然這目前看起來不是特別有用,但好處并不小。考慮這種情況。您正在使用 React 路由器,并且需要保護某些路由 – 如果用戶未經過身份驗證,則對這些路由的所有請求都應重定向到 /login。我們可以使用 HOC 來有效管理受保護的路由,而不是重復身份驗證代碼。好奇想知道怎么做嗎?我們將在下一個教程中介紹這一點以及更多內容。
注意:ecmascript 中提出了一個稱為裝飾器的功能,可以輕松使用 HOC。但是,它仍然是一個實驗性功能,因此我決定不在本教程中使用它。如果您使用的是 create-react-app,則需要先彈出才能使用裝飾器。如果您運行的是最新版本的 Babel (Babel 7),您所需要做的就是安裝 babel-preset-stage-0 然后將其添加到 webpack.config.dev.js 的插件列表中,如下所示。
// Process JS with Babel. { test: /.(js|jsx|mjs)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, presets: ['stage-0'] },
摘要
在本教程中,我們學習了 HOC 的基本概念。 HOC 是構建可重用組件的流行技術。我們首先討論基本的 ES6 語法,以便您更容易習慣箭頭函數并編寫現代 JavaScript 代碼。
然后我們了解了高階函數及其工作原理。最后,我們接觸了高階組件并從頭開始創建了 HOC。
接下來,我們將通過實際示例介紹不同的 HOC 技術。在那之前請繼續關注。在評論部分分享你的想法。