【推荐】react 最佳实践

帮助大家进一步了解React,帮助大家写出高效的可读性好的代码。

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方法只能返回一个节点,如果想输出多个节点,必须使用一个根节点将它们包裹起来。

【正确】:

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

【错误】:

return (
        <h2>title</h2>
        <div>content</div>
);
  1. 没有子节点的React组件,自闭合方式。

    【推荐】

    return <ComponentView/>;
    

    【不推荐】

    return <ComponentView></ComponentView>;
    
  2. 含有子节点的React组件

    【推荐】

    return (
            <ComponentView>
                    <ComponentChild/>
            </ComponentView>
    );
    
  3. 组件含有属性。

    【推荐】

    // 属性少,一行能放下放在一行
    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。

1、示例(原生组件-》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(); 

2、示例(自定义组件-》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);
        }

});

转载于:https://my.oschina.net/u/3047945/blog/806315

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值