React,用元件化思想寫前端程式碼

foreverpx發表於2015-09-02

前陣子嘗試用React開發了一個專案的前端,寫起來還算是流暢。將頁面中各模組進行分割並形成元件之後,管理起來更加的方便,程式碼的可讀性也相對於傳統的麵條式程式設計有很大的提高。React相對於Angular來說,我個人認為是更加輕量化的,它更注重於MVC中的V。

舉個例子,開發中常常會用到button這個元素,我們會給button加上樣式、行為等等。一個專案中button可能是被經常重複使用的,他的樣式也可能幾乎是一摸一樣的,無非是color和size的區別而已。按照傳統的寫法,可能程式碼是下面這個樣子的:

<button onClick='function(){alert('clicked!')}' class='btn btn-green btn-large'>點我</button>

如果click事件是有預設事件的,class預設也是綠色,那麼每次都寫這一堆是有點煩了,如果我們將這個button視為一個元件,那麼我們可以給它設定預設的樣式和行為,並通過傳不同的值給元件改變它的狀態。我們來個React版本的。

var Button = React.createClass({
    propTypes:{
        children: React.PropTypes.any,
        className: React.PropTypes.string,
        disabled: React.PropTypes.bool,
        onClick: React.PropTypes.func,
        style: React.PropTypes.object,
        type: React.PropTypes.oneOf(['submit', 'button'])
    },
    getInitialState:function(){
        return {
            disabled:this.props.disabled,
            className:'btn-green',
            type: this.props.type ? this.props.type : 'button',
            text: null
        }
    },
    componentWillReceiveProps:function(newProps){
        if (newProps.disabled !== this.props.disabled) {
            this.setState({ disabled: newProps.disabled })
        }
    },
    text:function(text){
        this.setState({
            text:text
        });
    },
    handleClick:function() {
        if (this.props.onClick) {
            this.props.onClick()
        }
    },
    render:function(){
        return (
            <button className={this.state.className}
                    disabled={this.state.disabled}
                    type={this.state.type}
                    style={this.props.style}
                    onClick={this.handleClick.bind(this)}>
                {this.state.text || this.props.children}
            </button>
        );
    }
});

這裡Button就視為一個模組或者元件,它接收父元件的值來改變它的預設狀態,當沒有值傳入時,使用預設的內部值。
為了達到最開始那個button的效果,我們可以這麼使用它:

React.render(
       <div>
           <Button size='large' onClick='function(){alert('click')}'>
       </div>
       , document.getElementById('container'));

雖然這樣看上去好像並沒有和傳統方式有太大的區別,而且還多了一堆程式碼去定義這個button元件,得不償失,但是這僅僅只是元件化的開始,面對更復雜的元件時,它的好處就體現出來了。看看下面的例子:

需求是我們需要一個搜尋框,在使用者輸入的同時通過非同步請求進行搜尋,同時為了防止使用者輸入速度快導致請求過於頻繁,在使用者輸入每個字後延遲300ms進行搜尋,如果在這段時間內使用者任然有輸入,那麼就再延遲300ms,也就是高頻防抖。

以傳統的方式來,我們得先寫個input,然後把這個input包裝成search input的樣子,然後註冊事件,並在事件中設定延遲函式,然後才真正執行搜尋動作。。。等另一個頁面又有搜尋時,重複剛才的步驟。如果把SearchInput設想成一個元件,那麼它會是下面這個樣子的。

var SearchInput = React.createClass({
    inputTimeout:null,
    propTypes:{
        size: React.PropTypes.string,
        onInput: React.PropTypes.func,
        delay: React.PropTypes.number,
        disabled: React.PropTypes.bool
    },
    getInitialState:function(){
        return {
            size: this.props.size ? this.props.size : '',
            delay: this.props.delay ? this.props.delay : 300
        }
    },
    handleInput: function(event){
        var value = event.target.value;
        if(this.inputTimeout){
            window.clearTimeout(this.inputTimeout);
        }
        var that = this;
        this.inputTimeout = setTimeout(function(){
            that.props.onInput(value);
        },this.state.delay);
    },
    render: function(){
        var sizeClasss = this.state.size ? "topcoat-search-input--"+this.state.size :
            "topcoat-search-input";
        return (
            <input type="search"
                   placeholder='search'
                   onInput={this.handleInput.bind(this)}
                   className={sizeClasss}
                   disabled={this.props.disabled}
                />
        );
    }
});

我們將延遲的時間和是否禁用等資訊當做屬性傳給SearchInput元件,並將延遲操作的邏輯等封裝在元件內部,以後我們就不用關心這些了,直接使用即可。

React.render(
       <div>
           <SearchInput delay=500 onClick='function(value){alert(value)}'></SearchInput>
       </div>
       , document.getElementById('container'));

我們傳入delay與搜尋邏輯函式即可,input的value也會傳入搜尋邏輯函式。

當然,還有更復雜的情況,我們可以將邏輯封裝在元件中,使用的人只需要通過屬性改變元件的狀態,而不需要關心元件狀態改變的邏輯。

最近嘗試封裝一些通用的React UI元件,挑選了Topcoat樣式去封裝,感興趣的朋友可以點選下面這個連結
https://github.com/ForeverPx/react-topcoat

文章作者:forevercjl
原文連結:www.foreverpx.cn
轉載請註明出處。

相關文章