React实践(一)

 

该实践取自官方教程:https://github.com/reactjs/react-tutorial

主要是自实现的过程以及一些心得体会

该实践是实现一个评论框。

  • 一个展示所有评论的视图
  • 一个提交评论的表单
  • 与后台的接口hook

特点:

  • 评论提交之前就先显示在列表中,提高体验
  • 其他用户的评论实时更新
  • 可用markdown格式编写文本

开始

下面就是我们的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 here
13 </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(this.loadCommentsFromServer, this.props.pollInterval);
20 },
21 render: function() {
22 return (
23 <div className="commentBox">
24 <h1>Comments</h1>
25 <CommentList data={this.state.data} />
26 <CommentForm />
27 </div>
28 );
29 }
30 });
31
32 React.render(
33 <CommentBox url="comments.json" pollInterval={2000} />,
34 document.getElementById('content')
35 );

关于显示的,我们就弄的差不多了。

接下来是我们的表单提交部分

添加新评论

我们的表单要求用户输入评论人和评论内容,当用户提交表单的时候,会把数据提交到服务器,然后保存这条数据。

 1 var CommentForm = React.createClass({
2 render: function() {
3 return (
4 <form className="commentForm">
5 <input type="text" placeholder="Your name" />
6 <input type="text" placeholder="Say something..." />
7 <input type="submit" value="Post" />
8 </form>
9 );
10 }
11 });

我们的表单是可交互的,所以这里要添加表单的提交事件,且刷新评论列表。为了更好的体验,在提交完表单之后,表单应该是清空了的。

 1 var CommentForm = React.createClass({
2 handleSubmit: function(e) {
3 e.preventDefault();
4 var author = this.refs.author.getDOMNode().value.trim();
5 var text = this.refs.text.getDOMNode().value.trim();
6 if (!text || !author) {
7 return;
8 }
9 // TODO: send request to the server
10 this.refs.author.getDOMNode().value = '';
11 this.refs.text.getDOMNode().value = '';
12 return;
13 },
14 render: function() {
15 return (
16 <form className="commentForm" onSubmit={this.handleSubmit}>
17 <input type="text" placeholder="Your name" ref="author" />
18 <input type="text" placeholder="Say something..." ref="text" />
19 <input type="submit" value="Post" />
20 </form>
21 );
22 }
23 });

其中,

  • 我们利用了ref属性给子组件命名,this.refs引用组件,getDOMNode()获取本地的DOM元素。
  • React使用驼峰命名的方式给组件绑定事件,我们给表单绑定了onSubmit()事件,当数据合法,清空输入框

当用户提交了表单后,我们需要添加我们的评论到评论列表。上面我们是给了commentBox一个state来保存评论列表。正是因为这样,我们的所有逻辑在commentBox中完成是最合理的。因为我们需要从子组件传回数据给父组件,这里我们把回调函数作为属性传给子组件。

 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 handleCommentSubmit: function(comment) {
15 // TODO: submit to the server and refresh the list
16 },
17 getInitialState: function() {
18 return {data: []};
19 },
20 componentDidMount: function() {
21 this.loadCommentsFromServer();
22 setInterval(this.loadCommentsFromServer, this.props.pollInterval);
23 },
24 render: function() {
25 return (
26 <div className="commentBox">
27 <h1>Comments</h1>
28 <CommentList data={this.state.data} />
29 <CommentForm onCommentSubmit={this.handleCommentSubmit} />
30 </div>
31 );
32 }
33 });

当用户提交表单的时候,调用回调函数:

 1 var CommentForm = React.createClass({
2 handleSubmit: function(e) {
3 e.preventDefault();
4 var author = this.refs.author.getDOMNode().value.trim();
5 var text = this.refs.text.getDOMNode().value.trim();
6 if (!text || !author) {
7 return;
8 }
9 this.props.onCommentSubmit({author: author, text: text});
10 this.refs.author.getDOMNode().value = '';
11 this.refs.text.getDOMNode().value = '';
12 return;
13 },
14 render: function() {
15 return (
16 <form className="commentForm" onSubmit={this.handleSubmit}>
17 <input type="text" placeholder="Your name" ref="author" />
18 <input type="text" placeholder="Say something..." ref="text" />
19 <input type="submit" value="Post" />
20 </form>
21 );
22 }
23 });

回调函数等一切都搞定,现在把提交到服务器的代码和刷新评论的代码补上来:

 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 handleCommentSubmit: function(comment) {
15 $.ajax({
16 url: this.props.url,
17 dataType: 'json',
18 type: 'POST',
19 data: comment,
20 success: function(data) {
21 this.setState({data: data});
22 }.bind(this),
23 error: function(xhr, status, err) {
24 console.error(this.props.url, status, err.toString());
25 }.bind(this)
26 });
27 },
28 getInitialState: function() {
29 return {data: []};
30 },
31 componentDidMount: function() {
32 this.loadCommentsFromServer();
33 setInterval(this.loadCommentsFromServer, this.props.pollInterval);
34 },
35 render: function() {
36 return (
37 <div className="commentBox">
38 <h1>Comments</h1>
39 <CommentList data={this.state.data} />
40 <CommentForm onCommentSubmit={this.handleCommentSubmit} />
41 </div>
42 );
43 }
44 });

代码基本上都已经完成了。

还有一个点,我们可以做的,就是为了提高体验,我们可以本地先把用户提交的评论显示出来,之后再异步提交到服务器,让用户觉得应用快快快。

 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 handleCommentSubmit: function(comment) {
15 var comments = this.state.data;
16 var newComments = comments.concat([comment]);
17 this.setState({data: newComments});
18 $.ajax({
19 url: this.props.url,
20 dataType: 'json',
21 type: 'POST',
22 data: comment,
23 success: function(data) {
24 this.setState({data: data});
25 }.bind(this),
26 error: function(xhr, status, err) {
27 console.error(this.props.url, status, err.toString());
28 }.bind(this)
29 });
30 },
31 getInitialState: function() {
32 return {data: []};
33 },
34 componentDidMount: function() {
35 this.loadCommentsFromServer();
36 setInterval(this.loadCommentsFromServer, this.props.pollInterval);
37 },
38 render: function() {
39 return (
40 <div className="commentBox">
41 <h1>Comments</h1>
42 <CommentList data={this.state.data} />
43 <CommentForm onCommentSubmit={this.handleCommentSubmit} />
44 </div>
45 );
46 }
47 });

其实就添加了15-17行。

简单吧~~~~~~~

这就是React官方提供的最基本的实践~~~~~~
看到了这里的都是真爱~~~~~~~~~

晚安~~~~~~~~

 
分类: React.js

最新文章

  1. php工作笔记6-手机端适应缩放
  2. 移动Windows用户文件夹的方法研究
  3. 整站网页doc下载wget (转)
  4. ACE - 代码层次及Socket封装
  5. win7中USB音箱没有声音解决的方法
  6. 关于$.fn
  7. R-大数据分析挖掘(2-R爬虫)
  8. mac 下svn降级
  9. SQL Serverf 索引 - 索引压缩 、附加特性 &lt;第十篇&gt;
  10. BZOJ 1455: 罗马游戏( 配对堆 + 并查集 )
  11. leetcode — surrounded-regions
  12. EF CodeFirst系列(4)--- 数据注释属性
  13. Mapreduce的序列化和流量统计程序开发
  14. 使用java注解实现toJson方法
  15. 关于eclipse启动报错,an error has occurred.see the log file
  16. iOS 调试大法
  17. Codeforces 1068 - A/B/C/D/E - (Done)
  18. Java EE之Struts2-2.5配置
  19. MYSQL IN 与 EXISTS 的优化示例介绍
  20. RabbitMQ学习笔记(一):安装及Springboot集成

热门文章

  1. python正文(两)
  2. mysql回想一下基础知识
  3. centos7安装并配置svn(转)
  4. JS学习笔记-OO疑问之封装
  5. Gradle 1.12 翻译——第十六章. 使用文件
  6. MVC 应用免受 CSRF攻击
  7. JSP 获得Spring 注射对象
  8. Deploy Oracle 10.2.0.5 on Red Hat Enterprise Linux 6.4
  9. mysql出现Data truncated for column
  10. 解决Virtual Box 安装Mac OS X当出现“hfs: summary table not allowed on FS with block size of 2048”问题