@brizer
2016-01-28T12:35:08.000000Z
字数 7795
阅读 1090
随着入门教程做一个评论组件出来。
首先是官网提供的源码地址。
首先新建public/index.html:
<!-- index.html --><!DOCTYPE html><html><head><meta charset="utf-8" /><title>React Tutorial</title><script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react-dom.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script></head><body><div id="content"></div><script type="text/babel" src="scripts/example.js"></script><script type="text/babel">// 如果需要自己加代码,可以将上面的example.js注释掉</script></body></html>
这里引用jquery不是说明react必须依赖jquery。
我们首先设计组件的结构:
CommentBoxCommentListCommentCommentForm
我们先定义一个简单的CommentBox:
<div id="content"></div><script type="text/babel">// 如果需要自己加代码,可以将上面的example.js注释掉var CommentBox = React.createClass({render: function(){return (<div className="commentBox">Hello,world!I am a CommentBox.</div>);}});React.render(<CommentBox />,document.getElementById("content"));</script>
效果如下:
我们使用的JSX语法在以后会详解。
在上面的代码中,我们通过React.createClass()来创建一个新的组件。其中最重要的方法是render,该方法返回一颗React组件树,这棵树最终将会演变成HTML。
接下来我们定义CommentList,CommentForm。
//CommentList组件var CommentList = React.createClass({render:function(){return (<div className="commentList">Hello,world!I am a CommentList.</div>);}});//CommentForm组件var CommentForm = React.createClass({render:function(){return (<div className="commentForm">Hello.world!I am a CommentForm.</div>);}});
我们把上面定义的两个组件组装到CommentBox中:
//CommentBox组件var CommentBox = React.createClass({render: function(){return (<div className="commentBox"><h1>Comments</h1><CommentList /><CommentForm /></div>);}});
效果如下:
组件拼接成功。
我们创建Comment组件,该组件依赖于从父级传入的数据。我们可以通过this.props访问到这些数据:
//Comment组件var Comment = React.createClass({render:function(){return (<div className="comment"><h2 className="commentAuthor">{this.props.author}</h2>{this.props.children}</div>);}});
接下来我们就可以对每一个独立的评论重用相同的代码了,先添加一些评论到CommentList:
//CommentList组件var CommentList = React.createClass({render:function(){return (<div className="commentList"><Comment author="刘放">这是刘放的评论</Comment><Comment author="tyq">这是 *tyq* 的评论</Comment></div>);}});
得到效果如下:
其中我们通过this.props.author和this.props.children 来访问属性。
需要用到第三方库:
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js"></script>
接下来进行转换输出:
//Comment组件var Comment = React.createClass({render:function(){return (<div className="comment"><h2 className="commentAuthor">{this.props.author}</h2>{marked(this.props.children.toString())}</div>);}});
效果如下:
可以看到结果并不是我们想要的,这样因为React在保护应用免受XSS攻击。我们进入如下修改:
//Comment组件var Comment = React.createClass({rawMarkup:function(){var rawMarkup = marked(this.props.children.toString(),{sanitize:true});return { __html:rawMarkup };},render:function(){return (<div className="comment"><h2 className="commentAuthor">{this.props.author}</h2><span dangerouslySetIneerHTML={this.rawMarkup()}/></div>);}});
我们传入sanitize:true,告诉marked转义掉评论文本中的HTML标签而不是直接原封不动地返回这些标签。
之前我们使用的是静态数据,现在我们模拟从后端传来json格式数据:
//模拟评论数据var data = [{author:"liufang",text:"good boy"},{author:"tyq",text:"good girl"}];
接下来我们在渲染时,将data绑定到属性:
ReactDOM.render(<CommentBox data={data}/>,document.getElementById("content"));
CommentBox组件汇总将data属性传递下去:
//CommentBox组件var CommentBox = React.createClass({render: function() {return (<div className="commentBox"><h1>Comments</h1><CommentList data={this.props.data} /><CommentForm /></div>);}});
最后在CommentList中进行属性对于赋值:
//CommentList组件var CommentList = React.createClass({render: function() {var commentNodes = this.props.data.map(function (comment) {return (<Comment key={comment.author} author={comment.author}>{comment.text}</Comment>);});return (<div className="commentList">{commentNodes}</div>);}});
通过map将data遍历,取author和text属性对应赋值。
效果如下
前面提到,组件根据props渲染自己,props是从父组件传递过来的不变的属性列表。为了实现交互,我们给组件引入了可变的state。this.state是组件私有的,可以通过调用this.setState()来改变,当state更新后,组件就会重新渲染。
我们先将数据存入data.json:
[{"author":"liufang","text":"good boy"},{"author":"tyq","text":"good girl"}]
然后在渲染时通过url:
ReactDOM.render(<CommentBox url="./data.json"/>,document.getElementById("content"));
将CommentBox组件改成通过ajax获取:
//CommentBox组件var CommentBox = React.createClass({//初始化组件的state设置,在组件生命中仅执行一次getInitialState:function(){return {data:[]};},//组件渲染时,React自动调用的方法componentDidMount:function(){$.ajax({url:this.props.url,dataType:'json',cache:false,success:function(data){//关键,将数据设置到视图this.setState({data:data});}.bind(this),error:function(xhr,status,err){console.error(this.props.url,status,err.toString());}.bind(this)});},render: function() {return (<div className="commentBox"><h1>Comments</h1><CommentList data={this.state.data} /><CommentForm /></div>);}});
上面的componentDidMount是一个组件渲染的时候被React自动调用的方法。其中的关键点就是调用this.setState(),在服务器数据改变时,对数据进行改变。
上面就可以得到正常的效果了。如果我们需要实现实时更新的话。需要进行以下改变,从而进行简单的轮询。
首先,在渲染时传入轮询时间:
ReactDOM.render(<CommentBox url="./data.json" pollInterval={2000} />,document.getElementById("content"));
然后对CommentBox进行改装,进行轮询:
//CommentBox组件var CommentBox = React.createClass({//初始化组件的state设置,在组件生命中仅执行一次getInitialState:function(){return {data:[]};},//请求服务器函数loadCommentsFromServer:function(){$.ajax({url:this.props.url,dataType:'json',cache:false,success:function(data){this.setState({data:data});}.bind(this),error:function(xhr,status,err){console.error(this.props.url,status,err.toString());}.bind(this)});},//组件渲染时,React自动调用的方法//进行轮询调用请求componentDidMount:function(){this.loadCommentsFromServer();setInterval(this.loadCommentsFromServer,this.props.pollInterval);},render: function() {return (<div className="commentBox"><h1>Comments</h1><CommentList data={this.state.data} /><CommentForm /></div>);}});
这样就可以达到轮询的效果。
前面的CommentList和Comment组件功能都基本完成了。接下来就是CommentForm组件,其功能是将评论的名字和内容保存到服务器。
//CommentForm组件var CommentForm = React.createClass({handleSubmit:function(e){e.preventDefault();var author = this.refs.author.value.trim();var text = this.refs.text.value.trim();if(!text||!author){return;}//发送到服务器//清空表单this.refs.author.value='';this.refs.text.value='';return;},render:function(){return (<form className="commentForm" onSubmit={this.handleSubmit}><input type="text" ref="author" placeholder="name" /><input type="text" ref="text" placeholder="say something" /><input type="submit" value="Post" /></form>);}});
效果如下:
当用户提交评论时,我们需要刷新评论列表来加进这条新评论。在CommentBox组件中完成这个逻辑,因为该组件拥有代表评论列表的state。
我们需要从子组件传数据到它的父组件。我们在父组件的render方法中这样做:传递一个新的回调函数handleCommentSubmit到子组件,绑定它到子组件的onCommentSubmit事件上。无论事件什么时候触发,回调函数都会被调用。
//CommentBox组件var CommentBox = React.createClass({//初始化组件的state设置,在组件生命中仅执行一次getInitialState:function(){return {data:[]};},//请求服务器函数loadCommentsFromServer:function(){$.ajax({url:this.props.url,dataType:'json',cache:false,success:function(data){this.setState({data:data});}.bind(this),error:function(xhr,status,err){console.error(this.props.url,status,err.toString());}.bind(this)});},//组件渲染时,React自动调用的方法//进行轮询调用请求componentDidMount:function(){this.loadCommentsFromServer();setInterval(this.loadCommentsFromServer,this.props.pollInterval);},//绑定到子组件的回调函数handleCommentSubmit:function(comment){//提交到服务器并刷新列表},render: function() {return (<div className="commentBox"><h1>Comments</h1><CommentList data={this.state.data} />//CommentForm表单提交事件<CommentForm onCommentSubmit={this.handleCommentSubmit} /></div>);}});
当用户提交表单时,在CommentForm中调用这个回调函数:
//CommentForm组件var CommentForm = React.createClass({handleSubmit:function(e){e.preventDefault();var author = this.refs.author.value.trim();var text = this.refs.text.value.trim();if(!text||!author){return;}//发送到服务器//清空表单//提交表单时调用回调函数this.props.onCommentSubmit({author:author,text:text});this.refs.author.value='';this.refs.text.value='';return;},render:function(){return (<form className="commentForm" onSubmit={this.handleSubmit}><input type="text" ref="author" placeholder="name" /><input type="text" ref="text" placeholder="say something" /><input type="submit" value="Post" /></form>);}});
剩下的就是将数据提交到服务器上去了。
终于把入门demo给做完了,对react的组件化开发大概也有了一定的了解。个人觉得入门还是比较简单。
下一次详细学习JSX语法吧。