React实践(一)
该实践取自官方教程:https://github.com/reactjs/react-tutorial
主要是自实现的过程以及一些心得体会
该实践是实现一个评论框。
- 一个展示所有评论的视图
- 一个提交评论的表单
- 与后台的接口hook
特点:
- 评论提交之前就先显示在列表中,提高体验
- 其他用户的评论实时更新
- 可用markdown格式编写文本
开始
下面就是我们的index.html模板文件,看官copy过去吧。之后的所有代码都写在script里面
<!-- index.html -->
<html>
<head>
<title>Hello React</title>
<script src="http://fb.me/react-0.13.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.13.0.js"></script>
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
// Your code here
</script>
</body>
</html>
其中jquery不是对React必要的,只是我们想简化ajax的代码
组件结构
React是全组件化的,可组装的。我们的组件结构如下:
- CommentBox
- CommentList
- Comment
- CommentForm
CommentBox
让我们先来把最基本的组件构造出来
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('content')
);
从代码不难看出,也不过是简单的div罢了
CommentList、CommentForm
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Hello, world! I am a CommentList.
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
这两个组件也不过就是div而已
那我们根据组件结构,把这两个组件放进CommentBox:
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
Comment
组件结构里,现在还剩CommentList里的Comment。我们想在评论中传评论人和评论文本过去。那我们来实现一下:
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="Pete Hunt">This is one comment</Comment>
<Comment author="Jordan Walke">This is *another* comment</Comment>
</div>
);
}
});
我们已经从父组件CommenList传递了一些数据给子组件Comment
那我们来实现Comment组件,看官应该还记得,我们通过this.props在子组件中获取数据:
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});
其中,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的调用
var converter = new Showdown.converter();
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{converter.makeHtml(this.props.children.toString())}
</div>
);
}
});
其中,为了转换成Showdown能处理的原始字符串,所以显示调用了toString()
但是React为了防止XSS,我们的显示会类似这样,<p>This is<em>another</em> comment</p>
并没有渲染成真正的HTML
当然我们这里有一个方法:
var converter = new Showdown.converter();
var Comment = React.createClass({
render: function() {
var rawMarkup = converter.makeHtml(this.props.children.toString());
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
注意:框架会警告你别使用这种方法
接入数据模型
上面,我们是把数据直接写入React中,实际开发中,数据是来自于数据库的,那我们这里就暂且用hard code的形式写在JSON对象中。
var data = [
{author: "Pete Hunt", text: "This is one comment"},
{author: "Jordan Walke", text: "This is *another* comment"}
];
我们需要把数据通过props的形式传到CommentList。而首先就是把数据传入到CommentBox
这里是React的单向数据流。关于这个,会在之后另开一篇文章来研究下。
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.props.data} />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox data={data} />,
document.getElementById('content')
);
这时候,CommentList就可以使用数据了。让我们来动态地去渲染评论,而不是之前一条一条地写<Comment >xxx</Comment>
来看下代码:
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
就这样了。
从数据库获取数据
实际开发中,往往是后台提供了数据接口,而这时候,我们就需要这个接口跟我们上面的实现结合起来了。
而且,现在的这个组件在请求数据回来之前,是没有数据的。并且,评论是需要更新的。
我们之前是通过data传给CommentBox,每个组件也只在初始化的时候更新一次。
props是不可变的,它们从父节点传过来,被父节点所拥有。而为了实现交互,这里就需要用到state,this.state是组件私有的,当state变化的时候,组件会重新渲染自己。
关于props和state,之后也会写一篇来具体介绍一下。
React.render(
<CommentBox url="comments.json" />,
document.getElementById('content')
);
因为,我们这里没有去实现后台,所以姑且用本地的文件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:
var CommentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
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>
);
}
});
这里我们给CommentBox添加了一个data数组的state,作为取到的评论的保存。
组件会在组件构建完后,去取数据,动态更新的要点就是this.setState()
而我们的评论是实时更新的,即别人如果在数据库里添加了评论,那我们是要实时去检测是否更新了的。
这里我们就简单的用轮训的方法:
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.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 className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox url="comments.json" pollInterval={2000} />,
document.getElementById('content')
);
关于显示的,我们就弄的差不多了。
接下来是我们的表单提交部分
添加新评论
我们的表单要求用户输入评论人和评论内容,当用户提交表单的时候,会把数据提交到服务器,然后保存这条数据。
var CommentForm = React.createClass({
render: function() {
return (
<form className="commentForm">
<input type="text" placeholder="Your name" />
<input type="text" placeholder="Say something..." />
<input type="submit" value="Post" />
</form>
);
}
});
我们的表单是可交互的,所以这里要添加表单的提交事件,且刷新评论列表。为了更好的体验,在提交完表单之后,表单应该是清空了的。
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
if (!text || !author) {
return;
}
// TODO: send request to the server
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});
其中,
- 我们利用了ref属性给子组件命名,this.refs引用组件,getDOMNode()获取本地的DOM元素。
- React使用驼峰命名的方式给组件绑定事件,我们给表单绑定了onSubmit()事件,当数据合法,清空输入框
当用户提交了表单后,我们需要添加我们的评论到评论列表。上面我们是给了commentBox一个state来保存评论列表。正是因为这样,我们的所有逻辑在commentBox中完成是最合理的。因为我们需要从子组件传回数据给父组件,这里我们把回调函数作为属性传给子组件。
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
// TODO: submit to the server and refresh the list
},
getInitialState: function() {
return {data: []};
},
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 onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
当用户提交表单的时候,调用回调函数:
var CommentForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return;
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});
回调函数等一切都搞定,现在把提交到服务器的代码和刷新评论的代码补上来:
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
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) {
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 className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
代码基本上都已经完成了。
还有一个点,我们可以做的,就是为了提高体验,我们可以本地先把用户提交的评论显示出来,之后再异步提交到服务器,让用户觉得应用快快快。
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
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) {
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 className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
其实就添加了15-17行。
简单吧~~~~~~~
这就是React官方提供的最基本的实践~~~~~~
看到了这里的都是真爱~~~~~~~~~
晚安~~~~~~~~
React实践(一)的更多相关文章
- React实践
React实践(一) 该实践取自官方教程:https://github.com/reactjs/react-tutorial 主要是自实现的过程以及一些心得体会 该实践是实现一个评论框. 一个展示 ...
- React 实践项目 (二)
React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...
- React 实践项目 (三)
React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 上回说到使用Redux进行 ...
- React 实践项目 (五)
React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! React 实践项目 (一 ...
- React 实践记录 02 Flux introduction
Introduction 本文组成: React 官方文档翻译 相关实践心得. 内容上是Flux的介绍,例子将会在以后写出. 一旦稍微多了解一点React,很难避免听到Flux这个名词. Flux是一 ...
- React 实践记录 01 组件开发入门
Introduction 本文组成: Ryan Clark文章Getting started with React的翻译. 博主的实践心得. React由Facebook的程序员创建,是一个非常强大的 ...
- React 实践心得:react-redux 之 connect 方法详解
Redux 是「React 全家桶」中极为重要的一员,它试图为 React 应用提供「可预测化的状态管理」机制. Redux 本身足够简单,除了 React,它还能够支持其他界面框架.所以如果要将 R ...
- React 实践项目 (一)
React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 项目代码地址:https: ...
- React 实践记录 04 Flux demo
Introduction flux应用架构如下图所示,本文并不是讲述怎么立即做一个酷炫的应用,而是讲述如何依照这种框架,来进行代码的组织. 我们先把这个流程转述为文字:抛开与webAPI的交互不谈,以 ...
随机推荐
- zoj 2874 & poj 3308 Paratroopers (最小割)
意甲冠军: 一m*n该网络的规模格.详细地点称为伞兵着陆(行和列). 现在,在一排(或列) 安装激光枪,激光枪可以杀死线(或塔)所有伞兵.在第一i安装一排 费用是Ri.在第i列安装的费用是Ci. 要安 ...
- earlysuspend调用过程
1. 电源管理的状态 Android的Linux内核为系统提供了4种电源状态,内核的源码为当中的3种定义了名字和相应的宏定义,名字定义在kernel/power/suspend.c中: constch ...
- node.js基础:数据存储
无服务器的数据存储 内存存储 var http = require('http'); var count = 0; //服务器访问次数存储在内存中 http.createServer(function ...
- 发展,需求驱动 · 一间 所见即所得
从需求不是一句空话.同样是在发展过程中真正的. 需求驱动,与极限编程的一些想法和测试驱动开发基本重合. 鉴于该网站的发展是一个比较流行的方向,我会从网站开始,阐述自己的"需求驱动的发展&qu ...
- akka.net与微软分布式框架Orleans
微软分布式框架Orleans开源了 开源地址: https://github.com/dotnet/orleans 昨天编译了一下,这个最新的Orleans安装程序(用github源码编译的) 下载地 ...
- Intent用法
Intent是android系统中的最佳男主角,Intent翻译成中文的意思是"意图",说白了就是"我想要...",也就是说眼下运行中的Activity想要请其 ...
- iOS pragma mark要使用
郝萌主倾心贡献,尊重作者的劳动成果.请勿转载. 假设文章对您有所帮助.欢迎给作者捐赠.支持郝萌主,捐赠数额任意.重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 简单的来说 ...
- .NET MVC学习笔记(一)
看了些关于MVC的资料,做一些MVC的笔记. 分解关注点 在MVC世界里有个很重要的观念--"分解关注点"(Separation of Concerns),指的是:当你进行软件开发 ...
- ZendStudio10.6.1如何安装最新的集成svn小工具?
选择Help菜单->Install New Software...在Work with输入http://subclipse.tigris.org/update_1.10.x,等待完成后,.除了S ...
- HDU 5059 Help him(细节)
HDU 5059 Help him 题目链接 直接用字符串去比較就可以,先推断原数字正确不对,然后写一个推断函数,注意细节,然后注意判掉空串情况 代码: #include <cstdio> ...