深入react 高階元件
阿新 • • 發佈:2018-11-25
高階元件:
返回值是一個函式的那麼一個函式 基本算是個類工廠方法
W (WrappedComponent) 是被包裹的 React.Component;而函式返回的 E (Enhanced Component) 則是新得到的 HOC,也是個 React.Component
使用場景:
hoc能做什麼 在大的維度上 HOC 能用於:
返回
了一個被包裹元件的 React Element。同時,將 HOC 接受到的屬性傳遞給了被包裹的元件,因此稱為
“屬性代理”
。
可以用屬性代理做些什麼?
const ppHOC=(currentLoggedInUser)=>(WrappedComponent) =>{ return class PP extends React.Component { render() { const newProps = { user: currentLoggedInUser } return <WrappedComponent {...newProps}/> } }}
通過 refs 訪問例項
可以通過
ref
訪問到
this
(被包裹元件的例項),但這需要
ref
所引用的被包裹元件執行一次完整的初始化 render 過程,這就意味著要從 HOC 的 render 方法中返回被包裹元件的元素,並讓 React 完成其一致性比較過程,而
ref
能引用該元件的例項就好了。
例子:下例中展示瞭如何通過 refs 訪問到被包裹元件的例項方法和例項本身
可用在應用echarts等第三方庫時候的場景
function refsHOC(WrappedComponent) { return class RefsHOC extends React.Component { proc(wrappedComponentInstance) { wrappedComponentInstance.method() } render() { const props = Object.assign({}, this.props, {ref: this.proc.bind(this)}) return <WrappedComponent {...props}/> } }}
該高階元件可以作為一個元件的方法使用 抽象 state 通過提供給被包裹元件的屬性和回撥,可以抽象 state,這非常類似於 smart 元件是如何處理 dumb 元件的。
例子:在下面這個抽象 state 的例子裡我們簡單的將 value 和 onChange 處理函式從 name 輸入框中抽象出來。
function ppHOC(WrappedComponent) { return class PP extends React.Component { constructor(props) { super(props) this.state = { name: '' } this.onNameChange = this.onNameChange.bind(this) } onNameChange(event) { this.setState({ name: event.target.value }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange } } return <WrappedComponent {...this.props} {...newProps}/> } }} 用起來可能會是這樣的: @ppHOCclass Example extends React.Component { render() { return <input name="name" {...this.props.name}/> }} 於是這個輸入框就自動成為了一個受控元件。 包裹元件 可以利用元件的包裹,實現樣式定義、佈局或其他目標。一些基礎用法可以由普通的父元件完成(參閱附錄B),但如前所述,用 HOC 可以更加靈活。 例子:為定義樣式而實現的包裹 function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { return ( <div style={{display: 'block'}}> <WrappedComponent {...this.props}/> </div> ) } }} 繼承反轉 function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { return super.render() } }} 被返回的 HOC 類(強化過的類) 繼承 了被包裹的元件。之所以被稱為“繼承反轉”是因為,被包裹元件並不去繼承強化類,而是被動的讓強化類繼承。通過這種方式,兩個類的關係看起來 反轉 了。 繼承反轉使得 HOC 可以用 this 訪問被包裹元件的例項,這意味著 可以訪問 state、props、元件生命週期鉤子,以及 render 方法 。 可以用繼承反轉做些什麼?
例子2:修改 render 輸出的元素樹 function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { const elementsTree = super.render() let newProps = {}; if (elementsTree && elementsTree.type === 'input') { newProps = {value: 'may the force be with you'} } const props = Object.assign({}, elementsTree.props, newProps) const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children) return newElementsTree } }} 本例中,如果由 render 輸出的被包裹元件有一個 input 頂級元素,就改變其 value。 可以在這裡做任何事情,可以遍歷整個元素樹並改變其中的任何一個元素屬性。
操縱 state HOC 可以讀取、編輯和刪除被包裹元件例項的 state,也可以按需增加更多的 state。要謹記如果把 state 搞亂會很糟糕。大部分 HOC 應該限制讀取或增加 state,而後者(譯註:增加 state)應該使用名稱空間以免和被包裹元件的 state 搞混。
例子:對訪問被包裹元件的 props 和 state 的除錯 export function IIHOCDEBUGGER(WrappedComponent) { return class II extends WrappedComponent { render() { return ( <div> <h2>HOC Debugger Component</h2> <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre> <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre> {super.render()} </div> ) } }} 該 HOC 將被包裹元件嵌入其他元素中,並顯示了其 props 和 state。 使用 HOC 時,就失去了被包裹元件原有的名字,可能會影響開發和除錯。 hoc總結:
- 屬性代理:由 HOC 操縱那些被傳遞給被包裹元件 W 的 props
- 繼承反轉:HOC 繼承被包裹元件 W
hoc能做什麼 在大的維度上 HOC 能用於:
- 程式碼重用和邏輯抽象
- render 劫持
- state 抽象和操縱
- 操縱屬性(props)
- 操縱屬性
- 通過 refs 訪問例項
- 抽象 state
- 包裹元件
const ppHOC=(currentLoggedInUser)=>(WrappedComponent) =>{ return class PP extends React.Component { render() { const newProps = { user: currentLoggedInUser } return <WrappedComponent {...newProps}/> } }}
function refsHOC(WrappedComponent) { return class RefsHOC extends React.Component { proc(wrappedComponentInstance) { wrappedComponentInstance.method() } render() { const props = Object.assign({}, this.props, {ref: this.proc.bind(this)}) return <WrappedComponent {...props}/> } }}
該高階元件可以作為一個元件的方法使用 抽象 state 通過提供給被包裹元件的屬性和回撥,可以抽象 state,這非常類似於 smart 元件是如何處理 dumb 元件的。
例子:在下面這個抽象 state 的例子裡我們簡單的將 value 和 onChange 處理函式從 name 輸入框中抽象出來。
function ppHOC(WrappedComponent) { return class PP extends React.Component { constructor(props) { super(props) this.state = { name: '' } this.onNameChange = this.onNameChange.bind(this) } onNameChange(event) { this.setState({ name: event.target.value }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange } } return <WrappedComponent {...this.props} {...newProps}/> } }} 用起來可能會是這樣的: @ppHOCclass Example extends React.Component { render() { return <input name="name" {...this.props.name}/> }} 於是這個輸入框就自動成為了一個受控元件。 包裹元件 可以利用元件的包裹,實現樣式定義、佈局或其他目標。一些基礎用法可以由普通的父元件完成(參閱附錄B),但如前所述,用 HOC 可以更加靈活。 例子:為定義樣式而實現的包裹 function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { return ( <div style={{display: 'block'}}> <WrappedComponent {...this.props}/> </div> ) } }} 繼承反轉 function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { return super.render() } }} 被返回的 HOC 類(強化過的類) 繼承 了被包裹的元件。之所以被稱為“繼承反轉”是因為,被包裹元件並不去繼承強化類,而是被動的讓強化類繼承。通過這種方式,兩個類的關係看起來 反轉 了。 繼承反轉使得 HOC 可以用 this 訪問被包裹元件的例項,這意味著 可以訪問 state、props、元件生命週期鉤子,以及 render 方法 。 可以用繼承反轉做些什麼?
- render 劫持
- 操縱 state
- 在任何 render 輸出的 React Elements 上增刪查改 props
- 讀取並修改 render 輸出的 React Elements 樹
- 條件性顯示元素樹
- 出於定製樣式的目的包裹元素樹(正如屬性代理中展示的)
例子2:修改 render 輸出的元素樹 function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { const elementsTree = super.render() let newProps = {}; if (elementsTree && elementsTree.type === 'input') { newProps = {value: 'may the force be with you'} } const props = Object.assign({}, elementsTree.props, newProps) const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children) return newElementsTree } }} 本例中,如果由 render 輸出的被包裹元件有一個 input 頂級元素,就改變其 value。 可以在這裡做任何事情,可以遍歷整個元素樹並改變其中的任何一個元素屬性。
操縱 state HOC 可以讀取、編輯和刪除被包裹元件例項的 state,也可以按需增加更多的 state。要謹記如果把 state 搞亂會很糟糕。大部分 HOC 應該限制讀取或增加 state,而後者(譯註:增加 state)應該使用名稱空間以免和被包裹元件的 state 搞混。
例子:對訪問被包裹元件的 props 和 state 的除錯 export function IIHOCDEBUGGER(WrappedComponent) { return class II extends WrappedComponent { render() { return ( <div> <h2>HOC Debugger Component</h2> <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre> <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre> {super.render()} </div> ) } }} 該 HOC 將被包裹元件嵌入其他元素中,並顯示了其 props 和 state。 使用 HOC 時,就失去了被包裹元件原有的名字,可能會影響開發和除錯。 hoc總結:
- render 劫持(在繼承反轉中看見過)
- 控制內部 props(同樣在繼承反轉中看見過)
- 抽象 state,但存在缺點。將無法在外部訪問父元素的 state,除非特意為止建立鉤子。這限制了其實用性
- 包裹新的 React Elements。這可能是父元件唯一強於 HOC 的用例,雖然 HOC 也能做到
- 操縱子元件有一些陷阱。比如說如果 children 的根一級並不只有單一的子元件(多於一個的第一級子元件),你就得新增一個額外的元素來收納所有子元素,這會讓你的程式碼有些冗繁。在 HOC 中一個單一的頂級子元件是被 React/JSX 的約束所保證的。
轉自:https://mp.weixin.qq.com/s/dtlrOGTjoneOIiM5kB3XvQ(部分理解稍做修改)