学习链接:http://reactjs.cn/react/docs/tutorial-zh-CN.html
实现功能:主要分两个部分:1、评论列表的展示 2、评论框的输入提交
一、组件
1、拆分组件
一个大容器CommentBox中包含CommetList和CommetForm,CommetList中又包含多个Commet组件
2、CommentBox组件
var CommentBox = React.createClass({ render: function(){
return(
<div>
<CommentList />
<CommentForm />
</div>
)
}
})
ReactDOM.render({
<CommetBox />,
document.getElementById('content')
})
//首先在CommentBox中添加两个组件,然后将CommetBox渲染到页面上
- React组件必须用大写字母开头,且标签必须闭合,通过return函数返回的标签也必须是闭合的。比如我将<CommentList/>和<CommentForm/>外面的div去掉,就会报错
- React.createClass 是ES5形式的定义组件;后面又有了React.Component ,ES6形式的定义组件。(React定义组件的方法http://www.cnblogs.com/wonyun/p/5930333.html)
- ReactDOM.render({element, container, callback})渲染组件的方法,接收三个参数element 要渲染的组件,container渲染的组件要插入在DOM中的位置,callback回调函数,可选参数,传入时会在组件渲染完成后调用
- render React生命周期中的初始化阶段的一个函数,用来组装组件的html结构,必须要有返回值,返回的也可以是null 或者false
3、CommentList组件
var CommetList= React.createClass({ render: function(){ return( <div> //包含多个comment组件 <Comment /> <Comment /> </div> ) } })
4、Comment组件
var Comment = React.createClass({
render: function(){ return( <div> <h3>"this is author"</h3> //展示作者
<div>"this is comment text"</div> //展示评论 </div> ) } })
5、CommentForm组件
var CommentForm = React.createClass({ render: function(){ return(
<form> <input type='text' placeholder='enter your name'/>
<input type='text' placeholder='say something ...'/>
<input type='submit' value='submit'/>
</form> ) } })
二、数据
1、简介
React父子组件数据的传递可使用props和state。props属性是组件与生俱来的不可以由组件自己修改,state状态是事物自己的,可不断变化。主要区分是组件在运行时需要修改的是state,此外所有的数据都可以是props
属性 | 状态 | |
是否能从父组件获取初始值 | T | F |
是否由父组件修改 | T | F |
能否在组件内部设置默认值 | T | T |
能否在组件内部修改 | F | T |
能否设置子组件的初始值 | T | F |
能否修改子组件的值 | T | F |
(1)、this.props 用来访问从父组件传进来的数据
(2)、this.state 用来访问动态更新的数据
(3)、this.setState() 将服务器获取的数据更新到状态中(更新CommetList中的数据)
(4)、父子组件之间的数据通信,父组件通过this.props将数据传递给子组件;子组件通过委托(delegate)即事件处理函数与父组件通信,子组件触发函数,父组件处理。
2、获取、更新数据
ReactDOM.render(
<CommetBox url='/api/comments', pollInterval={2000}>, //这个请求地址是在server中设置好了,
document.getElementById('content')
)
var CommentBox = React.createClass({
loadCommentsFromServer: function(){
$.ajax({
url: this.props.url, //通过this.props获取父组件中的属性
dataType: 'json',
cache: false,
success: function(data){
this.setState({data:data}) //动态更新的关键是通过this.setState()设置状态
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
})
},
getInitialState:function(){
return {data:[]}
},
componentDidMount: function(){
this.loadCommentsFromServer(); //这里通过设置定时器实时刷新数据,为了避免刚开始数据要在间隔时间后才出来的情况,所以先调用一次获取数据
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function(){
return(
<div>
<CommentList data={this.state.data} />
<CommentForm />
</div>
)
}
})
var CommentList = React.creatClass({
render: function(){
var commnetNodes = this.props.data.map(function(comment){ //data的数据结构[ {"author": "Pete Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"} ]
return(
<Comment key={comment.id} author={comment.author}>
{commnet.text}
</Comment>
)
})
return(
<div>
{commentNodes}
</div>
)
}
})
var Comment = React.createClass({
return(
<div>
<h3>
{this.props.author}
</h3>
<p>
{this.children.text.toString()}
</p>
</div>
)
})
涉及到的生命周期:
(1)getInitialState 生命周期初始化阶段,初始化每个实例特有的状态,在生命周期里只执行一次,必须返回一个对象或者null
(2)componentDidMount 生命周期初始化阶段,在成功render并渲染完真实的DOM后react自动调用的方法,此时可以修改DOM。如果要在渲染后对DOM操作就是在这个阶段里。
三、事件
子组件通过事件与父组件通信,子组件触发,父组件处理。
1、添加评论,绑定事件
基本的操作在子组件CommentForm中完成
var CommentForm = React.createClass({ getInitialState:function(){ return({author:'',text:''}) }, handleAuthorChange: function(e){ this.setState({author: e.target.value}) //通过this.setState()保证输入实时更新状态 }, handleTextChange: function(e){ this.setState({text: e.target.value}) }, handleSubmit: function(e){ //提交事件,清空表单 e.preventDefault(); //阻止提交表单的默认行为 var author = this.state.author.trim(); var text = this.state.text.trim(); if (!author || !text) { return } this.setState({author:'',text:''}) }, render:function(){ return( <form onSubmit={this.handleSubmit}> //提交表单 <input type='text' value={this.state.author} placeholder='your name' onChange = {this.handleAuthorChange} //绑定事件 /> <input type='text' value={this.state.text} placeholder='enter something' onChange = {this.handleTextChange} //绑定事件 /> <input type='submit' value='提交' /> </form> ) } })
2、提交更新评论 子组件传递数据给父组件
提交评论时需要刷新评论列表来包含这条评论。发送请求及刷新评论的操作在CommentBox(即父组件)中完成,原因是:CommentBox中有评论列表的状态。现在需要把CommentForm(子组件)中的数据传递到CommentBox(父组件)中,子组件通过事件与父组件通信。所以在父组件中传递一个回调函数(handleCommetSubmit)到子组件中,子组件绑定这个函数传值即可,这样只要子组件CommentForm中的事件被触发,父组件CommentBox中的回调函数handCommentSubmit就会被调用。
//CommentBox中
handleCommentSubmit: function(comment){ $.ajax({ url:this.props.url, dataType:'json', type:'POST', data: comment, success: function(data){ this.setState({data:data}) }.bind(this), error: function(xhr, status, err){ this.setState({data: comments}); console.log(this.props.url, status, err.toString()) }.bind(this) })
render: function(){
return(
<div>
<CommentList data={this.state.data}/>
<CommentForm onCommentSubmit={this.handleCommentSubmit}/> //传递回调函数给子组件
</div>
)
}
//CommentForm中
handleSubmit: function(e){
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!author || !text) {
return
}
this.props.onCommentSubmit({author:author,text:text}) //绑定回调函数,则handleSubmit事件一触发,回调函数就会被调用
this.setState({author:'',text:''})
},
四、优化
1、更新太慢要等请求完成后页面才会展示新的评论,手动添加新评论到列表中让体验更快一些
handleCommentSubmit: function(comment) { var comments = this.state.data; comment.id = Date.now(); var newComments = comments.concat([comment]);this.setState({data: newComments}); $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { this.setState({data: comments}); console.error(this.props.url, status, err.toString()); }.bind(this) }); }
2、插入的文本中如果有HTML标签,我们是默认展示成输入的样子的,如点<a href='.....'>这里</a>可以跳转哦。如果我们想要把这种样子的评论真正的渲染成HTML,可以用到dangerouslySetInnerHTML
<span dangerouslySetInnerHTML={{ __html: this.props.children.toString()}}/>