定義:參數(shù)為組件的函數(shù)
功能: 為原始組件注入props (變量 方法)
定義
const EnhancedComponent = higherOrderComponent(WrappedComponent);
我們平時在react中編寫和使用的組件模式:
接收props-> 通過component -> 輸出UI
高階組件的模式:
接收componentA -> 通過HOC(純函數(shù) 無副作用
) -> 輸出新的componentB
HOC 并不是一個新的React Api,而是基于React的組合特性而形成的一種設計模式
具體而言,高階組件是參數(shù)為組件,返回值為新組件的函數(shù)
當兩個組件業(yè)務邏輯一樣,渲染的結果數(shù)據(jù)源不一致,(以前是用mixins
),我們需要把業(yè)務邏輯抽象,在一個地方定義這個業(yè)務邏輯,在各個組件之間共享,這就是高階組件.
使用原則
1.HOC 不要修改傳入的組件, 采用組合的方式
官方文檔多次強調: HOC和WrappedComponent的交互僅僅通過props 這樣用的好處
1.替換HOC(修改業(yè)務邏輯)變得很容易,只需要提供相同的props(更改數(shù)據(jù)源很容易).
2.對包裹組件類型無要求, 適用于class 組件和函數(shù)組件
- HOC和包裹組件之間 HOC和HOC 之間都可以任意組合
HOC 在 React 的第三方庫中很常見煤蹭,例如 Redux 的 connect 往組件注入props
HOC 與容器組件模式
之間有相似之處箭窜。容器組件擔任分離將高層和低層關注的責任忍些,由容器管理訂閱和狀態(tài)撬统,并將 prop 傳遞給處理渲染 UI缝呕。HOC 使用容器作為其實現(xiàn)的一部分两曼,你可以將 HOC 視為參數(shù)化容器組件
2.盡量提供和包裹組件一樣的props
HOC自己的狀態(tài)不要透傳給包裹組件,當我們要替換HOC的時候更加容易
render() {
// extraProp 為當前HOC狀態(tài) 不需要透傳給WrappedComponent 需要篩選出來
const { extraProp, ...passThroughProps } = this.props;
const injectedProp = someStateOrInstanceMethod;
// 其他 props 全部透傳給 WrappedComponent
return ( <WrappedComponent injectedProp={injectedProp} {...passThroughProps} /> );
}
3.最大組合性
單參數(shù)HOC
const NavbarWithRouter = withRouter(Navbar);
多參數(shù)HOC
const CommentWithRelay = Relay.createContainer(Comment, config);
最常見HOC 高階函數(shù)
高階函數(shù) React Redux 的 connect
函數(shù) 一個返回高階組件的高階函數(shù)皂甘!
//原始寫法
const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
// 翻譯過來
// connect 是一個高階函數(shù),它的返回值為另外一個函數(shù)
const enhance = connect(commentListSelector, commentListActions);
// 返回值為 HOC合愈,它會返回已經(jīng)連接 Redux store 的組件
const ConnectedComment = enhance(CommentList);
connet高階函數(shù)返回一個單參數(shù)的高階組件具有 component -> component特性,輸出類型與輸入類型相同的函數(shù)很容易組合,對于單參數(shù)的高階組件,HOC可以和HOC組合
const EnhancedComponent = withouter(connet(store)(componentA))
組合函數(shù) compose
// 原始寫法
const EnhancedComponent = withouter(connet(store)(componentA))
// ... 你可以編寫組合工具函數(shù)
// compose(f, g, h) 等同于 (...args) => f(g(h(...args)))
// 把輸出型==輸入型的函數(shù)(HOC)都組合起來
const enhance = compose([ withouter, connet(store) ])
const EnhancedComponent = enhance(componentA)
很多庫都提供compose 函數(shù) Composes functions from right to left.
右邊方法的出參 -> 作為左邊方法的入?yún)?(輸出類型與輸入類型相同)
1.redux compose 專為中間件設計 compose(applyMiddleware(thunk), DevTools.instrument())
2.lodash flow 和 flowright 為方法調用順序設計
裝飾器 Decorators
文檔鏈接: https://www.tslang.cn/docs/handbook/decorators.html
// 也可以再進一步寫為裝飾器模式
@withouter @connet(store) componentA
注意事項
不要在 render 方法中使用 HOC
render() {
// 每次調用 render 函數(shù)都會創(chuàng)建一個新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 這將導致子樹每次渲染都會進行卸載叮贩,和重新掛載的操作!
return <EnhancedComponent />;
}
會導致EnhancedComponent頻繁的掛載,卸載,這不僅僅是性能問題 - 重新掛載組件會導致該組件及其所有子組件的狀態(tài)丟失佛析。
靜態(tài)方法記得復制
// 定義靜態(tài)函數(shù)
WrappedComponent.staticMethod = function() {/*...*/}
// 現(xiàn)在使用 HOC
const EnhancedComponent = enhance(WrappedComponent);
// 增強組件沒有 staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
// 記得拷貝staticMethod
Enhance.staticMethod = WrappedComponent.staticMethod;
由于返回的是一個新的組件,包裹組件的靜態(tài)方法不會被繼承,必須要手動拷貝.
Refs 不會被傳遞
高階組件約定會報所有的props都傳給包裹組件,但是ref不會,ref實際上不是prop-就像 key
一樣益老,它是由 React 專門處理的
高階組件每次都返回一個新的組件,只能在組件聲明式包裹,不利于diff和動態(tài)傳參,render-props完美的解決了這個問題,直接將一個組件作為props傳遞進去
這兩種方式在react樹很大的時候,都會產(chǎn)生回調地獄,所以衍生了 reacthooks