React实践(一)
2015-06-23 00:20
253 查看
该实践取自官方教程:https://github.com/reactjs/react-tutorial
主要是自实现的过程以及一些心得体会
该实践是实现一个评论框。
一个展示所有评论的视图
一个提交评论的表单
与后台的接口hook
特点:
评论提交之前就先显示在列表中,提高体验
其他用户的评论实时更新
可用markdown格式编写文本
开始
下面就是我们的index.html模板文件,看官copy过去吧。之后的所有代码都写在script里面
其中jquery不是对React必要的,只是我们想简化ajax的代码
组件结构
React是全组件化的,可组装的。我们的组件结构如下:
CommentBox
让我们先来把最基本的组件构造出来
从代码不难看出,也不过是简单的div罢了
CommentList、CommentForm
这两个组件也不过就是div而已
那我们根据组件结构,把这两个组件放进CommentBox:
Comment
组件结构里,现在还剩CommentList里的Comment。我们想在评论中传评论人和评论文本过去。那我们来实现一下:
我们已经从父组件CommenList传递了一些数据给子组件Comment
那我们来实现Comment组件,看官应该还记得,我们通过this.props在子组件中获取数据:
其中,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的调用
其中,为了转换成Showdown能处理的原始字符串,所以显示调用了toString()
但是React为了防止XSS,我们的显示会类似这样,
并没有渲染成真正的HTML
当然我们这里有一个方法:
注意:框架会警告你别使用这种方法
接入数据模型
上面,我们是把数据直接写入React中,实际开发中,数据是来自于数据库的,那我们这里就暂且用hard code的形式写在JSON对象中。
我们需要把数据通过props的形式传到CommentList。而首先就是把数据传入到CommentBox
这里是React的单向数据流。关于这个,会在之后另开一篇文章来研究下。
这时候,CommentList就可以使用数据了。让我们来动态地去渲染评论,而不是之前一条一条地写<Comment >xxx</Comment>
来看下代码:
就这样了。
从数据库获取数据
实际开发中,往往是后台提供了数据接口,而这时候,我们就需要这个接口跟我们上面的实现结合起来了。
而且,现在的这个组件在请求数据回来之前,是没有数据的。并且,评论是需要更新的。
我们之前是通过data传给CommentBox,每个组件也只在初始化的时候更新一次。
props是不可变的,它们从父节点传过来,被父节点所拥有。而为了实现交互,这里就需要用到state,this.state是组件私有的,当state变化的时候,组件会重新渲染自己。
关于props和state,之后也会写一篇来具体介绍一下。
因为,我们这里没有去实现后台,所以姑且用本地的文件comments.json来返回这个JSON对象,就创建一个comments.json文件放在index.html同目录下,把下面的复制进去:
这里我们用的是JQ的AJAX,下面是修改后的CommentBox:
这里我们给CommentBox添加了一个data数组的state,作为取到的评论的保存。
组件会在组件构建完后,去取数据,动态更新的要点就是this.setState()
而我们的评论是实时更新的,即别人如果在数据库里添加了评论,那我们是要实时去检测是否更新了的。
这里我们就简单的用轮训的方法:
关于显示的,我们就弄的差不多了。
接下来是我们的表单提交部分
添加新评论
我们的表单要求用户输入评论人和评论内容,当用户提交表单的时候,会把数据提交到服务器,然后保存这条数据。
我们的表单是可交互的,所以这里要添加表单的提交事件,且刷新评论列表。为了更好的体验,在提交完表单之后,表单应该是清空了的。
其中,
我们利用了ref属性给子组件命名,this.refs引用组件,getDOMNode()获取本地的DOM元素。
React使用驼峰命名的方式给组件绑定事件,我们给表单绑定了onSubmit()事件,当数据合法,清空输入框
当用户提交了表单后,我们需要添加我们的评论到评论列表。上面我们是给了commentBox一个state来保存评论列表。正是因为这样,我们的所有逻辑在commentBox中完成是最合理的。因为我们需要从子组件传回数据给父组件,这里我们把回调函数作为属性传给子组件。
当用户提交表单的时候,调用回调函数:
回调函数等一切都搞定,现在把提交到服务器的代码和刷新评论的代码补上来:
代码基本上都已经完成了。
还有一个点,我们可以做的,就是为了提高体验,我们可以本地先把用户提交的评论显示出来,之后再异步提交到服务器,让用户觉得应用快快快。
其实就添加了15-17行。
简单吧~~~~~~~
这就是React官方提供的最基本的实践~~~~~~
看到了这里的都是真爱~~~~~~~~~
晚安~~~~~~~~
主要是自实现的过程以及一些心得体会
该实践是实现一个评论框。
一个展示所有评论的视图
一个提交评论的表单
与后台的接口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官方提供的最基本的实践~~~~~~
看到了这里的都是真爱~~~~~~~~~
晚安~~~~~~~~
相关文章推荐
- ReactiveCocoa入门教程
- ReactJS -- 初学入门
- ReactNavtive框架教程(1)
- React Diff 算法
- ReactJS入门(一)—— 初步认识React
- react组件的生命周期
- React.js终探(七)(完)
- React.js终探(六)
- 【Facebook的UI开发框架React入门之七】多View布局与样式Style简介(iOS平台)-goodmao
- UVA 10604--Chemical Reaction+记忆化搜索
- React-Todos
- React.js终探(五)
- ReactiveCocoa框架菜鸟入门(五)——信号的FlattenMap与Map
- 【Facebook的UI开发框架React入门之六】单个View的布局与样式Style简介(iOS平台)-goodmao
- ReactiveCocoa & MVVM 学习总结二
- 如何建立基于CocoaPods的ReactiveCocoa工程
- 【Facebook的UI开发框架React入门之五】视图View与文本Text简介(iOS平台)-goodmao
- ReactiveCocoa & MVVM 学习总结一
- proactor 与 actor 模式
- React.js再探(四)