前段時間在工做中寫Hybrid頁面時遇到了這樣的一個場景,公司須要一系列的活動組件,在每一個組件註冊的時候都須要調用App端提供的一個接口。一開始也考慮了幾種方式,包括mixin、組件繼承以及react高階組件。但通過了種種衡量,最後選擇使用了高階組件的作法。
React官方已不推薦使用Mixins的技術來實現代碼的重用,Mixins技術有一系列的缺點,首先Mixins會形成命名衝突,咱們經過如下的方式來注入Mixins:javascript
var myMixins = require('myMixins'); var Button = React.createClass({ mixins: [myMixins], // ... })
若是你須要注入多個mixins,其中一個是本身的,另外的多是第三方的。那有可能在兩個mixins裏使用了相同名稱的方法,這會使得其中的一個不起做用,而你能作的只有修改其中一個方法的名稱。另外一方面,一個mixins一開始多是很是簡單的,僅僅須要實現某一個功能,但當業務越加的複雜,須要往其中加入更多的方法的時候,就會變得很是複雜。要深刻了解mixins的缺點,能夠查看官方博客。html
對於我本身來講這種方法之前使用的比較多,先建立一個BaseComponent,在其中實現一系列公共的方法,其後的每一個組件都繼承於這個組件,但缺點是不夠靈活,在基礎組件中只能實現一些比較固定的方法,而對於每一個組件的定製化會有很大的限制。java
因爲mixins的一系列缺點,React官方也意識到使用mixins所帶來的痛點遠遠高於技術自己產生的優勢,而高階組件即可以代替mixins,並且當深刻以後它還有着更加豐富的用法。react
高階組件(HOC)是React中對組件邏輯進行重用的高級技術。但高階組件自己並非React API。它只是一種模式,這種模式是由React自身的組合性質必然產生的。
說到高階組件,就先得說到高階函數了,高階函數是至少知足下列條件的函數:ios
一、接受一個或多個函數做爲輸入
二、輸出一個函數
在javascript這門函數爲一等公民的語言中,高階函數的使用仍是很是之多的,像咱們平時的回調函數等等,都用到了高階函數的知識。咱們先來看一個簡單的高階函數git
var fun = function(x, y) { return x + y; }
fun是一個函數,下面咱們將整個函數做爲參數傳遞給另外一個函數github
var comp = function(x, y, f) { return f(x,y); }
驗證一下redux
comp(1,2,fun) // 3
類比高階函數的定義,高階組件就是接受一個組件做爲參數,在函數中對組件作一系列的處理,隨後返回一個新的組件做爲返回值。app
咱們先定義一個高階組件BaseActivitydom
const BaseActivity = (WrappedComponent) => { return class extends Component { render() { return ( <section> <div>個人包裹組件</div> <WrappedComponent /> </section> ) } } }
組件接受一個被包裹的組件做爲參數,返回了一個通過處理的匿名組件。
在其餘組件中使用這個高階組件
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函數來包裹這個組件,看下輸出的react dom內容
在Example組件外面包裹了一個匿名組件。
既然高階組件是一個函數,咱們就能夠向裏面傳遞咱們須要的參數
const BaseActivity = (WrappedComponent, title) => { return class extends Component { render() { return ( <section> <div>{title}</div> <WrappedComponent /> </section> ) } } }
在Example中這樣export
export default BaseActivity(Example, '這是高階組件的參數');
咱們看下輸出的react dom
能夠看到參數已經傳遞進去了。
固然還能夠這樣用(柯里化)
const BaseActivity (title) => (WrappedComponent) => { return class extends Component { render() { return ( <section> <div>{title}</div> <WrappedComponent /> </section> ) } } }
在Example中這樣export
export default BaseActivity('這是高階組件的參數')(Example);
這種用法在ant-design的表單以及redux的connect中咱們均可以看到
// ant const WrappedDemo = Form.create()(Demo) // redux export default connect(mapStateToProps, mapDispatchToProps)(Counter)
高階組件還能夠擴展原組件的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
高階組件也有一系列的缺點,首先是被包裹組件的靜態方法會消失,這其實也是很好理解的,咱們將組件當作參數傳入函數中,返回的已經不是原來的組件,而是一個新的組件,原來的靜態方法天然就不存在了。若是須要保留,咱們能夠手動將原組件的方法拷貝給新的組件,或者使用hoist-non-react-statics之類的庫來進行拷貝。
高階函數對於初學者來講可能不太好理解,但當你深刻其中,瞭解其中的原理以後,咱們可使用高階函數來完成不少的工做。
若是喜歡就給個Star吧,^_^