【推薦】react 最佳實踐

幫助你們進一步瞭解React,幫助你們寫出高效的可讀性好的代碼。html

React代碼編寫風格最佳實踐

React組件內方法推薦的書寫順序。

/**
 * @fileoverview  react組件內方法編寫順序
 */
define(function(require, exports, module) {

    var ReactComponent = React.createClass({
        propTypes: {},
        contextTypes: {},
        childContextTypes: {},

        getDefaultProps: function() {},
        getInitialState: function() {},
        getChildContext: function() {},
        
        // 組件生命週期方法
        componentWillMount: function() {},
        componentDidMount: function() {},
        componentWillReceiveProps: function() {},
        shouldComponentUpdate: function() {},
        componentWillUpdate: function() {},
        componentDidUpdate: function() {},
        componentWillUnmount: function() {},
        
        // 業務邏輯相關的處理方法
        loadData: function() {},
        parseData: function() {},
        handleClick: function() {},
            // 自動渲染虛擬DOM方法
        render: function() {}
    });

    module.exports = ReactComponent;
});
  • render 放在文件最後。由於咱們常常打開一個React組件,優先尋找的方法就是render,放在底部容易尋找。
  • 組件生命週期方法的生命週期執行順序編寫。便於理解以及問題的定位。

React組件JSX語法風格

  1. render方法只能返回一個節點,若是想輸出多個節點,必須使用一個根節點將它們包裹起來。

【正確】:react

return (
        <div>
                <h2>title</h2>
                <div>content</div>
        </div>
);

【錯誤】:git

return (
        <h2>title</h2>
        <div>content</div>
);
  1. 沒有子節點的React組件,自閉合方式。github

    【推薦】ajax

    return <ComponentView/>;

    【不推薦】數組

    return <ComponentView></ComponentView>;
  2. 含有子節點的React組件promise

    【推薦】異步

    return (
            <ComponentView>
                    <ComponentChild/>
            </ComponentView>
    );
  3. 組件含有屬性。ui

    【推薦】this

    // 屬性少,一行能放下放在一行
    return <ComponentView className="list-wrap" rel="list"/>;
    
    // 屬性多,>3個。多行顯示。
    return (
            <ComponentView
                    className="list-wrap"
                    rel="list"
                    onClick={this.handleClick}
                    ...
            /> 
    );
    
    // 若是屬性不少的話,推薦使用以下方式。把全部屬性放在一個對象裏面。
    var props = {
            className: 'list-wrap',
            rel: 'list',
            onSuccess: this.onSuccess
            onError: this.onSuccess
    };
    return <ComponentView {...props}/>;

    【不推薦】

    return <ComponentView className="list-wrap" 
            rel="list"/>;
  4. 子組件的渲染條件根據props\state的數據

    【推薦】:先聲明-》根據條件處理子組件-》{ReactElement}

    module.exports = React.createClass({
            getInitialState: function() {
                    return {
                            dataLoading: true
                    };
            },
            render: function() {
                    // 聲明一個變量。
                    var loading;
                    
                    if (this.state.dataLoading) {
                            loading = <Loading/>;
                    }
                    // 在JSX模板中,使用大括號{},引入子組件。
                    return (
                            <div>
                                    {loading}
                            </div>
                    );
            }
    });
  5. 遍歷數組經常使用方式。2種方式:map方法和forEach方法。

    【Array.map方法】

    var List = React.createClass({
            getDefaultProps: function() {
                    return {
                            data: [{
                                    name: 'test1'
                            },{
                                    name: 'test2'
                            }]
                    };
            },
            getInitialState: function() {
                    return {
                            data : this.props.data
                    };
            },
            render: function(){
                    return (
                            <ul>
                                    {
                                            this.state.data.map(function(item, index){
                                                    return <li>{item.name}</li>
                                            })
                                    }
                            </ul>
                    );
            }
     });

    【Array.forEach方法】

    var List = React.createClass({
            getDefaultProps: function() {
                    return {
                            data: [{
                                    name: 'test1'
                            },{
                                    name: 'test2'
                            }]
                    };
            },
            getInitialState: function() {
                    return {
                            data : this.props.data
                    };
            },
            render: function(){
                    var items;
    
                    this.state.data.forEach(function(item, index){
                            items.push(<li>{item.name}</li>);
                    })
    
                    return (
                            <ul>
                                    {items}
                            </ul>
                    );
            }
     });
  6. 若是想在this.setState以後立馬獲取新的state的值或者進行一些操做,能夠在回調中執行

    this.setState({
            coin: 111
    }, funtion(){
            console.log(this.state); // 新的state對象
            // 其餘指定操做
    })      
    console.log(this.state); // 舊的state對象

獲取DOM節點

refs
getDOMNode()

使用步驟:

  1. 在原生組件(HTML標籤上)使用 ref="name" 屬性對一個 DOM節點進行標記
  2. 經過 this.refs.name.getDOMNode() 獲取到這個節點的原生 DOM。

一、示例(原生組件-》HTML標籤):

獲取原生DOM節點以後,就能夠使用它擁有的屬性以及方法。

<input ref="myInfo" data-id="xxx"/>

var input = this.refs.myInfo.getDOMNode();

// 獲取value
var inputValue = input.value; 
// 自定義屬性
var inputAttr = input.getAttributes('data-id');
// 失去焦點
input.blur(); 
// 得到焦點
input.focus();

二、示例(自定義組件-》React組件):

<List ref="list" data={data} />

var list = this.refs.list.getDOMNode(); 

// 修改子組件實例的狀態,觸發render
list.setState({
        data: newData
});
// 能夠使用子組件實例中的方法
...

【注意】:

因爲 this.refs.name.getDOMNode() 獲取到是真實的DOM,必須等虛擬DOM節點插入文檔中後,才能用到這個屬性,不然就會報錯。
【推薦】:能夠在 componentDidMount 方法中調用、存儲DOM節點。
【錯誤使用】:在render方法中訪問refs屬性。報錯!!

componentWillUnmount

使用場景:移除組件前,這裏能夠作一些清除工做,例如清除內存,解除事件的監聽等等。

被觸發的條件:

  1. 父組件不用該子組件時候(null),子組件會被移除。子組件的生命週期方法componentWillUnmount會被觸發。
  2. 或者子組件父容器調用ReactDOM.unmountComponentAtNode(container)。container中的全部組件都會被卸載,全部子組件的componentWillUnmount 方法都會觸發。
  3. 子組件隱藏,或不可見時組件不卸載,componentWillUnmount 方法不會觸發的

例如:

var Loading = React.createClass({
                getDefaultProps: function(){
                        return {
                                text: '加載中'     
                        };
                },
                componentWillUnmount: function(){
                        console.log('componentWillUnmount');
                },
                render: function(){
                        return (
                                <div className="loading">{this.props.text}</div>
                        );
                }
        });

module.exports = React.createClass({
        getInitialState: function() {
                return {
                        dataLoading: true
                };
        },
        render: function() {
                var loading = null;
                if (this.state.dataLoading) {
                        loading = <Loading/>
                }
                return (
                        <div>
                                {loading}
                        </div>
                );
        }
});
// 當this.state.dataLoading = false時候,<Loading/>被卸載。<Loading/>組件中的componentWillUnmount在組件卸載前會被觸發。

數據

經過AJAX加載初始數據

【推薦】在componentDidMount 方法中異步加載初始數據。

var AjaxC = React.createClass({
        componentDidMount: function(){
                var self = this;
                var promise = $.ajax({
                        url:'',
                        type: 'GET'
                        data: ''        
                });
                promise.done(function(res){
                        if(res.errcode == 0) {
                                // 請求成功,更新state,觸發render方法更新視圖UI
                                self.setState({
                                        data: res.data
                                })
                        }
                });
        },
        render: function(){
                //....
        }       
});

子組件數據依賴父組件,在父組件中,數據方法變動,要求子組件UI一塊兒更新

子組件的數據由父組件提供,父組件經過props將數據傳給子組件。

組件嵌套結構
        - ParentComponent
                - ChildComponent
  1. 實現方式:componentWillReceiveProps

    var ParentComponent = React.createClass({
    
            componentDidMount: function(){
                    var self = this;
                    var promise = $.ajax({
                            url:'',
                            type: 'GET'
                            data: ''        
                    });
                    promise.done(function(res){
                            if(res.errcode == 0) {
                                    // 請求成功,更新state,觸發render方法更新視圖UI
                                    self.setState({
                                            avatar: res.data
                                    })
                            }
                    });
            },
            render: function(){
                    return (
                            <div>
                                    <div>
                                            圖片地址: {this.state.avatar}
                                    </div>
                                    <ChildComponent avatar={this.state.avatar} ref="child"/>
                            </div>
                    );      
            }
    });
    
    var ChildComponent = React.createClass({
         getInitialState: function() {
             return this.copyPropsToState(this.props);
         },
         /**
          * 單獨一個方法。組件狀態處理,依賴props
          */
         copyPropsToState: function(props) {
             return {
                 avatar: props.avatar
             }
         },
    
         componentWillReceiveProps: function(nextProps) {
             this.setState(this.copyPropsToState(nextProps));
         },
         
            render: function(){
                    return (
                            <img ref="avatar" src={this.state.avatar}/>
                    );
            }
    });
  2. 實現方式2, 在父組件中調用this.refs.xxx.setState();

    var ParentComponent = React.createClass({
            
            componentDidMount: function(){
                    var self = this;
                    var promise = $.ajax({
                            url:'',
                            type: 'GET'
                            data: ''        
                    });
                    promise.done(function(res){
                            if(res.errcode == 0) {
                                    // 請求成功,更新state,觸發render方法更新視圖UI
                                    self.setState({
                                            avatar: res.data
                                    })
                                    self.refs.child.setState({
                                            avatar: res.data
                                    })
                            }
                    });
            },
            render: function(){
                    return (
                            <div>
                                    <div>
                                            圖片地址: {this.state.avatar}
                                    </div>
                                    <ChildComponent avatar={this.state.avatar} ref="child"/>
                            </div>
                    );      
            }
    });
    
    var ChildComponent = React.createClass({
            getInitialState: function() {
             return {
                 avatar: this.props.avatar || ""
             }
         },
            render: function(){
                    return (
                            <img ref="avatar" src={this.state.avatar}/>
                    );
            }
    });

【總結】:

  1. 方式1:父組件中,props發生變動的渠道只有一種方式的時候,推薦用這種方式。
  2. 方式2:父組件中,props發生變動的渠道存在多種方式的時候,推薦用這種方式。

事件綁定

在JSX中使用事件

  1. 使用React.createClass() 建立React組件:

    JSX中的事件自動綁定、而且在組件卸載的時候,自動解除綁定。

    React.createClass({
            render: function(){
                    return (
                            <input type="text" onClick={this.handleClick}/>   
                    );
            },
            handleClick: function(e){
                    // this爲當前組件實例對象。
                    this.setStata({
                            data: '11'
                    })
            }
    });
    
    // 若事件須要傳參數
    React.createClass({
            render: function(){
                    return (
                            <input type="text" onClick={this.handleClick.bind(this, '傳的參數')}>
                    );
            },
            /**
             * @param data 傳的參數
             * @param e event對象
             */
            handleClick: function(data, e){
                    console.log(data) // => "傳的參數"
    
                    // 獲取發生點擊事件的DOM節點,使用方式同之前。
                    var target = e.currentTarget;
                    
                    // this爲當前組件實例對象。
                    this.setStata({
                            data: '11'
                    })
            }
    });
  2. 擴展,暫時不須要掌握【ES6語法建立React組件】

    JSX中的事件,須要手動綁定到當前組件的實例對象上,使得事件方法中的this指向的是該組件實例對象.

<input type="text" onClick={this.handleClick.bind(this)}/>

其餘普通事件綁定

【推薦】:在 componentDidMount 方法中綁定普通的DOM事件,而且在componentWillUnmount 中移除這些事件

var Scroll = React.createClass({
        componentDidMount: function(){
                // 請求數據
                this.loadData();
                // 綁定事件
                this.bindEvent();
        },
        
        componentWillUnmount: function(){
                console.log('off scroll event');
        $(window).off('scroll');
        },
        
        loadData: function(){
                //...
        },
        
        bindEvent: function(){
                console.log('bind scroll event');
                $(window).on('scroll', function() {
                        // 事件滾動操做
                });
        },
        
        render: function(){
                
        }               
});

Context

https://facebook.github.io/react/docs/context-zh-CN.html

實踐場景:
App中全局信息,組件嵌套層級很深,處於深處的子組件須要用到頂級的數據。若數據經過props方式一級一級往下傳,很麻煩。能夠經過此方式,讓子組件訪問到該數據

// 1. 在上級組件中,經過childContextTypes屬性和getChildContext方法,定義組件可訪問的數據(還能夠是方法)。

var Parent = React.createClass({
        childContextTypes: {
                goldCoin: React.PropTypes.any,
                getColdCoin: React.PropTypes.any,
                updateGoldCoin:React.PropTypes.any, 
        },
        getChildContext: function(){
                return {
                        goldCoin: this.state.goldCoin, // 第一次渲染組件時,就固定了子組件獲取的值。
                        getGoldCoin: this.getGoldCoin, // 若是n數據是變動的,推薦使用方法去獲取
                        updateGoldCoin: this.updateGoldCoin
                }
        },
        updateGoldCoin: function(goldCoin){
        this.setState({
            goldCoin: goldCoin 
        });
    },
        getGoldCoin: function(){
        return Number(this.state.goldCoin);
    }

})
// 2. 子組件中,能夠經過this.context.xxx訪問上級頂級的數據或者方法

var Child = React.createClass({
        contextTypes: {
        goldCoin: React.PropTypes.any,
        getColdCoin: React.PropTypes.any,
        updateGoldCoin: React.PropTypes.any
    },
       
        ...
        render: function(){
                ...
        },
        ...
        updateGoldCoin: function(){
                var goldCoin = this.context.getGoldCoin();
                var newGoldCoin = '333333';
                this.context.updateGoldCoin(newGoldCoin);
        }

});

相關文章
相關標籤/搜索