原文來(lái)自 https://gist.github.com/sebmarkbage/ef0bf1f338a7182b6775
import { Component } from "React" ;
export const Enhance = (ComposedComponent) => class extends Component {
constructor() {
this.state = { data: null };
}
componentDidMount() {
this.setState({ data: 'Hello' });
}
render() {
return <ComposedComponent {...this.props} data={this.state.data} />;
}
};
Enhance
是一個(gè)方法,當(dāng)傳入一個(gè) Component(ComposedComponent
) 的時(shí)候,它將自動(dòng)為該 Component 進(jìn)行擴(kuò)展并返回新的類定義。上例中,就返回了一個(gè)擴(kuò)展的 Component
類,為構(gòu)造函數(shù)中添加了 state
,也在 React 生命周期函數(shù) componentDidMount
中添加了處理邏輯,而 render
方法則使用了傳入的參數(shù),完成了渲染。
新的業(yè)務(wù)邏輯由 Enhance
提供(通過(guò)返回新的 Component),傳入的 ComposedComponent
就像一個(gè)回調(diào)函數(shù)。看看怎么使用:
import { Component } from "React";
import { Enhance } from "./Enhance";
class MyComponent = class extends Component {
render() {
if (!this.props.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
export default Enhance(MyComponent); // Enhanced component`
MyComponent
就是一個(gè)高階組件(類似于高階函數(shù)-回調(diào)函數(shù)),負(fù)責(zé)對(duì)特定的數(shù)據(jù)進(jìn)行渲染。MyComponent
僅僅知道別人會(huì)把數(shù)據(jù)通過(guò) this.prop.data
傳進(jìn)來(lái),其他就都不關(guān)心了。可以看到,和 Mixins 的擴(kuò)展方式相比,MyComponent
的工作要輕松很多。
Mixins
在 React 中,Mixins 是傳統(tǒng)的為 Component 進(jìn)行擴(kuò)展的做法。Mixins 的做法很像傳統(tǒng)的命令式編程,即要擴(kuò)展的組件決定需要哪些擴(kuò)展(Mixins),以及了解所有擴(kuò)展(Mixins)的細(xì)節(jié),從而避免狀態(tài)污染。當(dāng) Mixins 多了之后,被擴(kuò)展組件需要維護(hù)的狀態(tài)和掌握的”知識(shí)”越來(lái)越多,因此也就越來(lái)越難維護(hù),因?yàn)樨?zé)任都被交給了”最后一棒”(Last Responsible Moment)。
而高階組件的思路則是函數(shù)式的,每一個(gè)擴(kuò)展(Enhance
)就是一個(gè)函數(shù),接受要擴(kuò)展的組件作為參數(shù)。如果要進(jìn)行 3 個(gè)擴(kuò)展,那么則可以級(jí)聯(lián),看起來(lái)就是:
const newComponent = Enhance3(Enhance2(Enhance1(MyComponent)));
高階組件的方式則使得每一個(gè) Enhance
以及被擴(kuò)展的組件都只關(guān)心自己手里那點(diǎn)事。Enhance
不知道別人會(huì)怎么用它,被擴(kuò)展的組件也不關(guān)心別人會(huì)怎么擴(kuò)展它。負(fù)責(zé)人是那個(gè)將它們連在一起的”水管工”,即最后寫串聯(lián)代碼的人。
高階組件的用法雖然用到了 ES6 的類繼承,但是實(shí)際上卻只是把它當(dāng)個(gè)工具使用,而不是真的借助于 OO 的繼承。在 React 中使用高階組件部分替代 Mixins,仍然是非常函數(shù)化的思維方式,即針對(duì) ”***轉(zhuǎn)換” ***編程。只不過(guò)是組件定義替代了函數(shù)而已。
Decorators
除了函數(shù)方式擴(kuò)展,通過(guò) ES7 草案中的 Decorator(https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841) 也是可以的。Decorator 可以通過(guò)返回特定的 descriptor 來(lái)”修飾” 類屬性,也可以直接”修飾”一個(gè)類。即傳入一個(gè)已有的類,通過(guò) Decorator 函數(shù)”修飾”成了一個(gè)新的類。那么我們之前的 Enhance
方法就可以直接被當(dāng)成 Decorator 來(lái)用了。
import { Component } from "react";
import { Enhance } from "./Enhance";
@Enhance
class MyComponent extends Component {
render() {
if (!this.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
export default MyComponent; // Enhanced component
用或不用 Decorator,只是習(xí)慣問(wèn)題。
一個(gè)真實(shí)世界的 Enhance 的例子(https://github.com/kriasoft/react-decorators)
用法代碼如下:
import React from 'react';
import withViewport from 'react-decorators/withViewport';
@withViewport
class MyComponent {
render() {
let { width, height } = this.props.viewport;
return <div>Viewport: {width + 'x' + height}</div>;
}
}
React.render(<MyComponent />, document.body);
withView
是一個(gè) Enhance 函數(shù),或者 Class Decorator,傳入一個(gè)被擴(kuò)展組件(MyComponent
),返回一個(gè)新的組件。新組建具有獲取窗口尺寸變化的能力(viewport),并且將viewport 通過(guò) this.props.viewport
傳遞給被擴(kuò)展組件(MyComponent)。