2016-10-15 30 views
0

當我讀到mixin是多麼糟糕,並且自己做出了一些不好的體驗之後,我想重構我的代碼以使用mixin混合來實現可重用性。反應mixin到組成重構

我想要一個組件發出事件並進行風格化。所以我有mixins EventEmitter,其中包含操作事件的函數和包含操作樣式的函數的Stylable。

var Styleable = { 
    addStyleProperty: function(styleProperty) { 
    ... 
    }, 
    ... 
}; 
var EventEmitter = { 
    addEventListener: function(eventName, func) { 
    }, 
    ... 
}; 

隨着混入爲例成分如下所示:

var Button = React.createClass({ 
    mixins = [EventEmitter, Styleable], 
    ... 
}); 

隨着組成我試圖創建的設置樣式和EventEmitter成分是這樣的:

var StylableComponent = React.createClass({ 
    addStyleProperty: ... 
    render: function() { 
    React.createElement(this.props.wrappedComponent, ...)?? 
    } 
} 
var EventEmitterComponent = ... 

但我不知道如何正確使用它們。我讀過,我必須使用這些組件作爲包裝,但我不知道如何實現這一點。我試圖像在上面例子的渲染函數中那樣做。我怎樣才能實現一個像mixin變體一樣的函數按鈕?所以,我只是通過所需的功能如下:

<Button is={[StyleableComponent, EventEmitterComponent]}/> 

或者我期待從組成錯誤的行爲?

回答

1

實質上,您要創建一個返回全新組件的函數。

爲了您EventEmitter,例如,它會是這個樣子:

import eventEmitter from 'your-event-emitter-location'; 
 

 
// This function takes a component as an argument, and returns a component. 
 
function eventEmitterWrapper(ComposedComponent) { 
 
    // This is the brand new "wrapper" component we're creating: 
 
    return class extends Component { 
 
    render() { 
 
     // We want to return the supplied component, with all of its supplied 
 
     // props, but we also want to "add in" our additional behaviour. 
 
     return (
 
     <ComposedComponent 
 
      {...this.props} 
 
      eventEmitter={eventEmitter} 
 
     /> 
 
    ); 
 
    } 
 
    } 
 
}

要使用它,你會做這樣的事情:

import Button from '../Button'; 
 
import eventEmitterWrapper from '../../HOCs/event-emitter'; 
 

 
class Home extends Component { 
 
    render() { 
 
    const EventEmitterButton = eventEmitterWrapper(Button); 
 

 
    return (
 
     <div> 
 
     <EventEmitterButton onClick={doSomething}> 
 
      Hello there! 
 
     </EventEmitterButton> 
 
     </div> 
 
    ) 
 
    } 
 
}

最後,你的按鈕內,你必須從道具獲得事件發射器:

const Button = ({ children, onClick, eventEmitter }) => { 
 
    // Let's say, for example, you want to emit an event whenever the 
 
    // button is clicked. You could do something like this: 
 
    const clickHandler = (ev) => { 
 
    onClick(ev); 
 
    
 
    if (eventEmitter) { 
 
     eventEmitter.emit('click', ev); 
 
    } 
 
    }; 
 
    
 
    return (
 
    <button onClick={clickHandler}> 
 
     {children} 
 
    <button> 
 
     
 
) 
 
}

注意:這是一個人爲的例子。這種模式對我來說有點臭; Button對事件發射器的內部工作知之甚多(例如,它不需要知道它有一個emit方法。如果你改變事件發射器的工作方式,你會有一堆「啞」組件更新)。

爲了解決這種氣味,我需要更多地瞭解你的用例,但我可能會讓由eventEmitterWrapper函數返回的類暴露簡單的特定方法(例如「emitEvent」,它將採用鼠標/鍵盤事件的單個參數)

希望它說明了如何使用高階組件!

補充閱讀:

+0

我已閱讀給定的來源,這就是爲什麼我試圖重構它。如果有一個我想添加的功能,比如EventEmitter,它工作得很好。但是,如果我想添加Styleable,我也有一個像'styleableWrapper'這樣的鏈,然後把已經包裝好的對我來說很臭的組件。我希望你明白這一點。我也無法再訪問'eventEmitterWrapper'中聲明的函數。 – landunder

+0

高階組件可以包裝更高階的組件;它根本不臭(這只是功能組成!)。我認爲只要沒有相互依賴關係......如果'styleableWrapper'依賴於'eventEmitterWrapper',它會變得有點臭,但聽起來並不是這樣。 –

+0

爲了您對無法訪問在eventEmitterWrapper中聲明的函數的評論,需要將它們作爲道具傳遞下來。因爲每個HOC都會傳遞所有委託的道具,所以您傳遞的任何內容都將由'{... this.props}'收集。 –