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

React实践的更多相关文章

  1. React 实践项目 (二)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...

  2. React 实践项目 (三)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 上回说到使用Redux进行 ...

  3. React 实践项目 (五)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...

  4. React实践(一)

    该实践取自官方教程:https://github.com/reactjs/react-tutorial 主要是自实现的过程以及一些心得体会 该实践是实现一个评论框. 一个展示所有评论的视图 一个提交评 ...

  5. React 实践记录 02 Flux introduction

    Introduction 本文组成: React 官方文档翻译 相关实践心得. 内容上是Flux的介绍,例子将会在以后写出. 一旦稍微多了解一点React,很难避免听到Flux这个名词. Flux是一 ...

  6. React 实践记录 01 组件开发入门

    Introduction 本文组成: Ryan Clark文章Getting started with React的翻译. 博主的实践心得. React由Facebook的程序员创建,是一个非常强大的 ...

  7. React 实践心得:react-redux 之 connect 方法详解

    Redux 是「React 全家桶」中极为重要的一员,它试图为 React 应用提供「可预测化的状态管理」机制. Redux 本身足够简单,除了 React,它还能够支持其他界面框架.所以如果要将 R ...

  8. React 实践项目 (一)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 项目代码地址:https: ...

  9. React 实践记录 04 Flux demo

    Introduction flux应用架构如下图所示,本文并不是讲述怎么立即做一个酷炫的应用,而是讲述如何依照这种框架,来进行代码的组织. 我们先把这个流程转述为文字:抛开与webAPI的交互不谈,以 ...

随机推荐

  1. 机械革命X5(MECHREVO MR-X5)开包检验

    不废话.直接的问题,左右X5没有更具体的信息.为了通过有机会了解后续的选择,具体的数据被释放约: 首先看包装: 1.快递包裹,基于卖方这可以是不同的,包装各不相同 watermark/2/text/a ...

  2. SendMail如何签名

    MailAddress类有两个参数 第1个参数:发送者的邮箱 第2个参数:发送者的签名 示例: MailMessage message = new MailMessage();message.From ...

  3. 安装nodejs搭建ghost个人博客

    记录一下今天在linux下折腾nodejs的过程,相互学习: (不知道为什么用源代码安装,调用npm的时候老是会出现找不到npmlog的 错误,后来换用下了个二进制 来搞,假设那个牛牛知道为什么.希望 ...

  4. Source Insight 3.X 插件支持utf8,完美解决中国乱码,连接到美丽的轮廓

    上次SI多标签插件之后,由于公司内部编码改为utf8编码,因此特意做了这个Source Insight 3.X utf8插件. 下载地址:http://pan.baidu.com/s/1mgyZous ...

  5. A*算法进入

    作者文章链接:http://www.policyalmanac.org/games/aStarTutorial.htm 启示式搜索:启示式搜索就是在状态空间中的搜索对每个搜索的位置进行评估,得到最好的 ...

  6. cocos2dx3.1-lua移植android流程

    我很懒惰,写这篇博客只是为了能够转出后,当忘记查看,所以我写了下面非常简单的内容.假设完全没有经验的学生请找另一篇文章 一.环境配置(win7): 用户变量如下面: ANDROID_SDK_ROOT: ...

  7. C语言标准库函数qsort具体解释

    1 函数简单介绍 功 能: 使用高速排序例程进行排序 头文件:stdlib.h 用 法: void qsort(void *base,int nelem,int width,int (*fcmp)(c ...

  8. javascript事件和事件处理

    于js期间事件处理被分成三个步骤: 1.发生事件 2.启动事件处理程序 3.事件处理程序做出反应 事件处理程序的调用 1.在javascript中 在javascript中调用事件处理程序,首先要获得 ...

  9. url参数中出现+、空格、=、%、&、#等字符的解决办法

    url出现了有+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值,如何是好?解决办法将这些字符转化成服务器可以识别的字符,对应关系如下:URL字符转义 用其它 ...

  10. 非阻塞IOserver型号

    让我们来考虑一个场景,你和百万玩家的魔兽世界的忠实粉丝.时间之旅打每到周末boss. 每当周末比赛server在亚历山大,因为至少在同一时间数十万用户在线. 假设我们的多-threaded果酱serv ...