前段時間在工作中寫Hybrid頁面時遇到了這樣的一個場景筐乳,公司需要一系列的活動組件隘马,在每個組件注冊的時候都需要調(diào)用App端提供的一個接口。一開始也考慮了幾種方式扫步,包括mixin魔策、組件繼承以及react高階組件。但經(jīng)過了種種衡量河胎,最后選擇使用了高階組件的做法闯袒。
1、Mixins的缺點
React官方已不推薦使用Mixins的技術(shù)來實現(xiàn)代碼的重用游岳,Mixins技術(shù)有一系列的缺點政敢,首先Mixins會造成命名沖突,我們通過以下的方式來注入Mixins:
var myMixins = require('myMixins');
var Button = React.createClass({
mixins: [myMixins],
// ...
})
如果你需要注入多個mixins,其中一個是自己的胚迫,另外的可能是第三方的喷户。那有可能在兩個mixins里使用了相同名稱的方法,這會使得其中的一個不起作用访锻,而你能做的只有修改其中一個方法的名稱褪尝。另一方面,一個mixins一開始可能是非常簡單的期犬,僅僅需要實現(xiàn)某一個功能河哑,但當(dāng)業(yè)務(wù)越加的復(fù)雜,需要往其中加入更多的方法的時候龟虎,就會變得非常復(fù)雜灾馒。要深入了解mixins的缺點,可以查看官方博客遣总。
2睬罗、組件繼承
對于我自己來說這種方法以前使用的比較多,先創(chuàng)建一個BaseComponent旭斥,在其中實現(xiàn)一系列公共的方法容达,其后的每個組件都繼承于這個組件,但缺點是不夠靈活垂券,在基礎(chǔ)組件中只能實現(xiàn)一些比較固定的方法花盐,而對于每個組件的定制化會有很大的限制羡滑。
3、React高階組件
由于mixins的一系列缺點算芯,React官方也意識到使用mixins所帶來的痛點遠(yuǎn)遠(yuǎn)高于技術(shù)本身產(chǎn)生的優(yōu)點柒昏,而高階組件便可以代替mixins,而且當(dāng)深入之后它還有著更加豐富的用法熙揍。
高階組件(HOC)是React中對組件邏輯進(jìn)行重用的高級技術(shù)职祷。但高階組件本身并不是React API。它只是一種模式届囚,這種模式是由React自身的組合性質(zhì)必然產(chǎn)生的有梆。
高階函數(shù)
說到高階組件,就先得說到高階函數(shù)了意系,高階函數(shù)是至少滿足下列條件的函數(shù):
1泥耀、接受一個或多個函數(shù)作為輸入
2、輸出一個函數(shù)
在javascript這門函數(shù)為一等公民的語言中蛔添,高階函數(shù)的使用還是非常之多的痰催,像我們平時的回調(diào)函數(shù)等等,都用到了高階函數(shù)的知識迎瞧。我們先來看一個簡單的高階函數(shù)
var fun = function(x, y) {
return x + y;
}
fun是一個函數(shù)夸溶,下面我們將整個函數(shù)作為參數(shù)傳遞給另一個函數(shù)
var comp = function(x, y, f) {
return f(x,y);
}
驗證一下
comp(1,2,fun) // 3
高階組件定義
類比高階函數(shù)的定義,高階組件就是接受一個組件作為參數(shù)夹攒,在函數(shù)中對組件做一系列的處理蜘醋,隨后返回一個新的組件作為返回值。
我們先定義一個高階組件BaseActivity
const BaseActivity = (WrappedComponent) => {
return class extends Component {
render() {
return (
<section>
<div>我的包裹組件</div>
<WrappedComponent />
</section>
)
}
}
}
組件接受一個被包裹的組件作為參數(shù)咏尝,返回了一個經(jīng)過處理的匿名組件压语。
在其他組件中使用這個高階組件
class Example extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
width: '100%',
height: '100%'
}
}
componentWillMount() {
if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
return;
} else {
this.setState({
width: '375px',
height: '640px'
})
}
}
render() {
let { width, height } = this.state;
return (
<div className="activity">
<div className="activity-content" style={{ width, height }}>
<button className="btn">參加活動</button>
</div>
</div>
)
}
}
export default BaseActivity(Example);
具體用法就是在export 組件的時候,使用BaseActivity函數(shù)來包裹這個組件编检,看下輸出的react dom內(nèi)容
在Example組件外面包裹了一個匿名組件胎食。
參數(shù)
既然高階組件是一個函數(shù),我們就可以向里面?zhèn)鬟f我們需要的參數(shù)
const BaseActivity = (WrappedComponent, title) => {
return class extends Component {
render() {
return (
<section>
<div>{title}</div>
<WrappedComponent />
</section>
)
}
}
}
在Example中這樣export
export default BaseActivity(Example, '這是高階組件的參數(shù)');
我們看下輸出的react dom
可以看到參數(shù)已經(jīng)傳遞進(jìn)去了允懂。
當(dāng)然還可以這樣用(柯里化)
const BaseActivity (title) => (WrappedComponent) => {
return class extends Component {
render() {
return (
<section>
<div>{title}</div>
<WrappedComponent />
</section>
)
}
}
}
在Example中這樣export
export default BaseActivity('這是高階組件的參數(shù)')(Example);
這種用法在ant-design的表單以及redux的connect中我們都可以看到
// ant
const WrappedDemo = Form.create()(Demo)
// redux
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
高階組件還可以擴(kuò)展原組件的props屬性厕怜,如下所示:
const BaseActivity (title) => (WrappedComponent) => {
return class extends Component {
render() {
const newProps = {
id: Math.random().toString(8)
}
return (
<section>
<div>{title}</div>
<WrappedComponent {...this.props} {...newProps}/>
</section>
)
}
}
}
看下輸出的react dom
高階組件的缺點
高階組件也有一系列的缺點,首先是被包裹組件的靜態(tài)方法會消失蕾总,這其實也是很好理解的粥航,我們將組件當(dāng)做參數(shù)傳入函數(shù)中,返回的已經(jīng)不是原來的組件生百,而是一個新的組件递雀,原來的靜態(tài)方法自然就不存在了。如果需要保留蚀浆,我們可以手動將原組件的方法拷貝給新的組件缀程,或者使用hoist-non-react-statics之類的庫來進(jìn)行拷貝搜吧。
結(jié)語
高階函數(shù)對于初學(xué)者來說可能不太好理解,但當(dāng)你深入其中杨凑,了解其中的原理之后滤奈,我們可以使用高階函數(shù)來完成很多的工作。