您的位置:首页 > Web前端 > React

【推荐】react 最佳实践

2016-12-15 00:00 274 查看
摘要: 别人总结的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语法风格

render方法只能返回一个节点,如果想输出多个节点,必须使用一个根节点将它们包裹起来。

【正确】:

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

【错误】:

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


没有子节点的React组件,自闭合方式。
【推荐】
return <ComponentView/>;
【不推荐】
return <ComponentView></ComponentView>;


含有子节点的React组件
【推荐】
return (
<ComponentView>
<ComponentChild/>
</ComponentView>
);


组件含有属性。
【推荐】
// 属性少,一行能放下放在一行
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"/>;


子组件的渲染条件根据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>
);
}
});


遍历数组常用方式。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>
);
}
});


如果想在this.setState之后立马获取新的state的值或者进行一些操作,可以在回调中执行
this.setState({
coin: 111
}, funtion(){
console.log(this.state); // 新的state对象
// 其他指定操作
})
console.log(this.state); // 旧的state对象


获取DOM节点

refs
getDOMNode()

使用步骤:

在原生组件(HTML标签上)使用
ref="name"
属性对一个 DOM节点进行标记

通过
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

使用场景:移除组件前,这里可以做一些清除工作,例如清除内存,解除事件的监听等等。

被触发的条件:

父组件不用该子组件时候(null),子组件会被移除。子组件的生命周期方法
componentWillUnmount
会被触发。

或者子组件父容器调用
ReactDOM.unmountComponentAtNode(container)
。container中的所有组件都会被卸载,所有子组件的
componentWillUnmount
方法都会触发。

子组件隐藏,或不可见时组件不卸载,
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


实现方式:
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, 在父组件中调用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:父组件中,props发生变更的渠道只有一种方式的时候,推荐用这种方式。

方式2:父组件中,props发生变更的渠道存在多种方式的时候,推荐用这种方式。

事件绑定

在JSX中使用事件

使用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'
})
}
});


扩展,暂时不需要掌握【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);
}


});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ReactJS