该实践取自官方教程:https://github.com/reactjs/react-tutorial
主要是自实现的过程以及一些心得体会
该实践是实现一个评论框。
特点:
开始
下面就是我们的index.html模板文件,看官copy过去吧。之后的所有代码都写在script里面
1 <!-- index.html --> 2 <html> 3 <head> 4 <title>Hello React</title> 5 <script src="http://fb.me/react-0.13.0.js"></script> 6 <script src="http://fb.me/JSXTransformer-0.13.0.js"></script> 7 <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script> 8 </head> 9 <body>10 <div id="content"></div>11 <script type="text/jsx">12 // Your code here13 </script>14 </body>15 </html>
其中jquery不是对React必要的,只是我们想简化Ajax的代码
组件结构
React是全组件化的,可组装的。我们的组件结构如下:
- CommentBox - CommentList - Comment - CommentForm
CommentBox
让我们先来把最基本的组件构造出来
1 var CommentBox = React.createClass({ 2 render: function() { 3 return ( 4 <div className="commentBox"> 5 Hello, world! I am a CommentBox. 6 </div> 7 ); 8 } 9 });10 React.render(11 <CommentBox />,12 document.getElementById('content')13 );
从代码不难看出,也不过是简单的div罢了
CommentList、CommentForm
1 var CommentList = React.createClass({ 2 render: function() { 3 return ( 4 <div className="commentList"> 5 Hello, world! I am a CommentList. 6 </div> 7 ); 8 } 9 });10 11 var CommentForm = React.createClass({12 render: function() {13 return (14 <div className="commentForm">15 Hello, world! I am a CommentForm.16 </div>17 );18 }19 });
这两个组件也不过就是div而已
那我们根据组件结构,把这两个组件放进CommentBox:
1 var CommentBox = React.createClass({ 2 render: function() { 3 return ( 4 <div className="commentBox"> 5 <h1>Comments</h1> 6 <CommentList /> 7 <CommentForm /> 8 </div> 9 );10 }11 });
Comment
组件结构里,现在还剩CommentList里的Comment。我们想在评论中传评论人和评论文本过去。那我们来实现一下:
1 var CommentList = React.createClass({ 2 render: function() { 3 return ( 4 <div className="commentList"> 5 <Comment author="Pete Hunt">This is one comment</Comment> 6 <Comment author="Jordan Walke">This is *another* comment</Comment> 7 </div> 8 ); 9 }10 });
我们已经从父组件CommenList传递了一些数据给子组件Comment
那我们来实现Comment组件,看官应该还记得,我们通过this.PRops在子组件中获取数据:
1 var Comment = React.createClass({ 2 render: function() { 3 return ( 4 <div className="comment"> 5 <h2 className="commentAuthor"> 6 {this.props.author} 7 </h2> 8 {this.props.children} 9 </div>10 );11 }12 });
其中,this.props.children是任何内嵌的元素。
而前面说了,我们提供markdown格式的输入,那就修改一下。
添加Markdown
这里我们要用到第三方库Showdown,作用是处理Markdown文本且转换成原始HTML。
我们先添加<script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script>到head中
接着我们修改this.props.children,且添加Showdown的调用
1 var converter = new Showdown.converter(); 2 var Comment = React.createClass({ 3 render: function() { 4 return ( 5 <div className="comment"> 6 <h2 className="commentAuthor"> 7 {this.props.author} 8 </h2> 9 {converter.makeHtml(this.props.children.toString())}10 </div>11 );12 }13 });
其中,为了转换成Showdown能处理的原始字符串,所以显示调用了toString()
但是React为了防止XSS,我们的显示会类似这样,<p>
This is<em>
another</em>
comment</p>
并没有渲染成真正的HTML
当然我们这里有一个方法:
1 var converter = new Showdown.converter(); 2 var Comment = React.createClass({ 3 render: function() { 4 var rawMarkup = converter.makeHtml(this.props.children.toString()); 5 return ( 6 <div className="comment"> 7 <h2 className="commentAuthor"> 8 {this.props.author} 9 </h2>10 <span dangerouslySetInnerHTML={{__html: rawMarkup}} />11 </div>12 );13 }14 });
注意:框架会警告你别使用这种方法
接入数据模型
上面,我们是把数据直接写入React中,实际开发中,数据是来自于数据库的,那我们这里就暂且用hard code的形式写在JSON对象中。
1 var data = [2 {author: "Pete Hunt", text: "This is one comment"},3 {author: "Jordan Walke", text: "This is *another* comment"}4 ];
我们需要把数据通过props的形式传到CommentList。而首先就是把数据传入到CommentBox
这里是React的单向数据流。关于这个,会在之后另开一篇文章来研究下。
1 var CommentBox = React.createClass({ 2 render: function() { 3 return ( 4 <div className="commentBox"> 5 <h1>Comments</h1> 6 <CommentList data={this.props.data} /> 7 <CommentForm /> 8 </div> 9 );10 }11 });12 13 React.render(14 <CommentBox data={data} />,15 document.getElementById('content')16 );
这时候,CommentList就可以使用数据了。让我们来动态地去渲染评论,而不是之前一条一条地写<Comment >xxx</Comment>
来看下代码:
1 var CommentList = React.createClass({ 2 render: function() { 3 var commentNodes = this.props.data.map(function (comment) { 4 return ( 5 <Comment author={comment.author}> 6 {comment.text} 7 </Comment> 8 ); 9 });10 return (11 <div className="commentList">12 {commentNodes}13 </div>14 );15 }16 });
就这样了。
从数据库获取数据
实际开发中,往往是后台提供了数据接口,而这时候,我们就需要这个接口跟我们上面的实现结合起来了。
而且,现在的这个组件在请求数据回来之前,是没有数据的。并且,评论是需要更新的。
我们之前是通过data传给CommentBox,每个组件也只在初始化的时候更新一次。
props是不可变的,它们从父节点传过来,被父节点所拥有。而为了实现交互,这里就需要用到state,this.state是组件私有的,当state变化的时候,组件会重新渲染自己。
关于props和state,之后也会写一篇来具体介绍一下。
1 React.render(2 <CommentBox url="comments.json" />,3 document.getElementById('content')4 );
因为,我们这里没有去实现后台,所以姑且用本地的文件comments.json来返回这个JSON对象,就创建一个comments.json文件放在index.html同目录下,把下面的复制进去:
// comments.json[ {"author": "Pete Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"}]
这里我们用的是JQ的AJAX,下面是修改后的CommentBox:
1 var CommentBox = React.createClass({ 2 getInitialState: function() { 3 return {data: []}; 4 }, 5 componentDidMount: function() { 6 $.ajax({ 7 url: this.props.url, 8 dataType: 'json', 9 success: function(data) {10 this.setState({data: data});11 }.bind(this),12 error: function(xhr, status, err) {13 console.error(this.props.url, status, err.toString());14 }.bind(this)15 });16 },17 render: function() {18 return (19 <div className="commentBox">20 <h1>Comments</h1>21 <CommentList data={this.state.data} />22 <CommentForm />23 </div>24 );25 }26 });
这里我们给CommentBox添加了一个data数组的state,作为取到的评论的保存。
组件会在组件构建完后,去取数据,动态更新的要点就是this.setState()
而我们的评论是实时更新的,即别人如果在数据库里添加了评论,那我们是要实时去检测是否更新了的。
这里我们就简单的用轮训的方法:
1 var CommentBox = React.createClass({ 2 loadCommentsFromServer: function() { 3 $.ajax({ 4 url: this.props.url, 5 dataType: 'json', 6 success: function(data) { 7 this.setState({data: data}); 8 }.bind(this), 9 error: function(xhr, status, err) {10 console.error(this.props.url, status, err.toString());11 }.bind(this)12 });13 },14 getInitialState: function() {15 return {data: []};16 },17 componentDidMount: function() {18 this.loadCommentsFromServer();19 setInterval(
新闻热点
疑难解答