React Component Design - Higher-Order Component
- 發佈時間
Higher-Order Component
的簡稱叫HOC
,設計概念主要是要把一些共用的邏輯抽取出來,讓其他元件透過HOC
的方式能獲得這些共用邏輯的功能,讓元件的使用方式不一定要侷限在這個HOC
。
HOC
的概念很重要的一點是利用composition
的方式來產生一個新的component
,而不是直接mutate傳入的component
。
我們拿之前介紹過的Provider
來改寫成HOC試試
已複製!import React, {Fragment} from 'react' // copy static methods import hoistNonReactStatics from 'hoist-non-react-statics' import { Switch } from '../switch' const ToggleContext = React.createContext() class Toggle extends React.Component { static Consumer = ToggleContext.Consumer toggle = () => this.setState( ({ on }) => ({ on: !on }), () => this.props.onToggle(this.state.on), ) state = { on: false, toggle: this.toggle } render() { return ( <ToggleContext.Provider value={this.state} {...this.props} /> ) } } function withToggle(Component) { function Wrapper(props, ref) { return ( <Toggle.Consumer> {toggleContext => ( <Component toggle={toggleContext} {...props} ref={ref} /> )} </Toggle.Consumer> ) } Wrapper.displayName = `withToggle(${Component.displayName || Component.name})` return hoistNonReactStatics(React.forwardRef(Wrapper), Component) } const Layer1 = () => <Layer2 /> const Layer2 = withToggle(({ toggle: { on }}) => ( <Fragment> {on ? 'The button is on' : 'The button is off'} <Layer3 /> </Fragment> )) const Layer3 = () => <Layer4 /> const Layer4 = withToggle(({ toggle: { on, toggle }}) => ( <Switch on={on} onClick={toggle} /> )) function Usage({ onToggle = (...args) => console.log('onToggle', ...args), }) { return ( <Toggle onToggle={onToggle}> <Layer1 /> </Toggle> ) }
讓我們來看看HOC
幾個要注意的地方
displayName
需要設定- 傳入的
component
的static methods
需要copy到HOC
裡面新的component
,所以一般會透過hoist-non-react-statics
複製static methods
到新的component
- 如果有希望使用
ref
,需要透過React.forwardRef
的方式轉換
從上面幾點不難看出HOC
有滿多的缺點,而且在現在React 18的環境中,其實已經有一些方式可以替代HOC
,比如之前介紹過的Render Props
pattern,或者是hook
的方式。
讓我們先把上面的例子改寫成Render Props
的形式來看看
已複製!class ToggleWrapper extends React.Component { render() { return ( <Toggle.Consumer> {toggleContext => ( this.childen({ toggle: toggleContext }) )} </Toggle.Consumer> ) } }
Render Props
這樣的方式看起來差別不大,但優點其實很多
children
不受限制- 純粹是一個
React Component
,讓React tree
乾淨 - 可以寫
Prop Types
檢驗props
了! static methods
跟ref
的問題都同時解決了props
不用spred