利用React写一个评论区组件
本文是在阅读学习了官方的React Tutorial之后的整理,一个静态的评论区组件。
所以内容,和React官网一模一样,可查看官网源代码。
开始使用React
首先从官方获取React.js,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="build/react.js"></script> <script src="build/react-dom.js"></script> <script src="build/browser.min.js"></script> <script src="build/jquery.min.js"></script> <script src="https://npmcdn.com/remarkable@1.6.2/dist/remarkable.min.js"></script> </head> <body> <div id="content"></div> <script type="text/babel" src="./scripts/example.js"></script> </body> </html>
你的第一个组件
React 中都是关于模块化、可组装的组件。以我们的评论框为例,我们将有如下的组件结构:
- CommentBox - CommentList -Comment - CommentForm
通过React.createClass()可以一个React组件,我们可以像这样定义我们的CommentBox,并通过ReactDOM.render()方法可以让我们在指定的容器中将React元素渲染为一个DOM组件
var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1>
<CommentList />
<CommentForm /></div> ); } }); ReactDOM.render( <CommentBox />, document.getElementById('content') );
注意原生的HTML元素以小写开头,而制定的 React 类以大写开头。
从这个例子也可以看出一个组件可以包含子组件,组件之间是可以组合的(Composing),并呈现一个树形结构,也可以说render方法中的的 CommentBox代表的是组件树的根元素。那么接下来我们来创建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> ); } });
首先是CommentList组件,这个组件是用来呈现评论列表的,根据开始我们设计的组件结构树,这个组件应该是包含许多Comment子组件的,
var Comment = React.createClass({ render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {this.props.children} </div> ); } });
既然我们已经定义了 Comment
组件,我们将要传递作者名和评论文字给它。这允许我们为每个评论重用相同的代码。现在让我们在我们的 CommentList
里添加一些评论。
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> ); } });
注意,我们已经从 CommentList
组件传递了一些数据到 Comment
组件。例如,我们传递了 Pete Hunt (通过属性)和 This is one comment (通过 XML-风格的子节点)给第一个 Comment
。如上面提到的那样, Comment
组件将会通过 this.props.author
和 this.props.children
访问 这些 '属性'。
添加 Markdown #
Markdown 是一种简单的内联格式化你的文字的方法。例如,用星号包围文本将会使其强调突出。
在本教程中我们使用第三方库 remarkable,它接受 Markdown 文本并且转换为原始的 HTML。我们已经在初始的页面标记里包含了这个库,所以我们可以直接开始使用它,让我们转换评论文本为 Markdown 并输出它:
var Comment = React.createClass({ render: function() { var md = new Remarkable(); return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {md.render(this.props.children.toString())} </div> ); } });
我们在这里唯一做的就是调用 remarkable 库。我们需要把 从 React 的包裹文本来的 this.props.children
转换成 remarkable 能理解的原始字符串,所以我们显示地调用了toString()
。
但是这里有一个问题!我们渲染的评论在浏览器里看起来像这样: "<p>
This is <em>
another</em>
comment</p>
" 。我们想让这些标签真正地渲染为 HTML。
那是 React 在保护你免受 XSS 攻击。有一个方法解决这个问题,但是框架会警告你别使用这种方法:
var Comment = React.createClass({ rawMarkup: function() { var md = new Remarkable(); var rawMarkup = md.render(this.props.children.toString()); return { __html: rawMarkup }; }, render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> <span dangerouslySetInnerHTML={this.rawMarkup()} /> </div> ); } });
这是一个特殊的 API,故意让插入原始的 HTML 变得困难,但是对于 remarkable 我们将利用这个后门。
记住: 使用这个功能你会依赖于 remarkable 是安全的。
那么,假设我们已经获取到评论数据了:
var data = [ {author: "Pete Hunt", text: "This is one comment"}, {author: "Jordan Walke", text: "This is *another* comment"} ];
我们需要把数据传递给CommentList组件才能让它去呈现,那么如何传递呢?我们可以通过this.props来访问组件标签上的属性,比如我们在CommentBox组件的代码中做如下修改:
var CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.props.data} /> <CommentForm /> </div> ); } }); ReactDOM.render( <CommentBox data={data} />, document.getElementById('content') );
然现在数据在 CommentList
中可用了,让我们动态地渲染评论:
var CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function(comment) { return ( <Comment author={comment.author} key={comment.id}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); } });
好了,接下来我们的CommentList算是完成了,我们需要加上CommentForm组件让我们可以提交评论:
var CommentForm = React.createClass({ getInitialState: function() { return {author: '', text: ''}; }, handleAuthorChange: function(e) { this.setState({author: e.target.value}); }, handleTextChange: function(e) { this.setState({text: e.target.value}); }, handleSubmit: function(e) { e.preventDefault(); var author = this.state.author.trim(); var text = this.state.text.trim(); if (!text || !author) { return; } this.props.onCommentSubmit({author: author, text: text}); // TODO: send request to the server this.setState({author: '', text: ''});//清空文本框里内容 }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" value={this.state.author} onChange={this.handleAuthorChange}/> <input type="text" placeholder="Say something..." value={this.state.text} onChange={this.handleTextChange} /> <input type="submit" value="Post" /> </form> ); } });
我们发现到现在为止,我们的页面是静态的,但我们希望可以在成功提交了评论后可以立刻在评论列表中看到自己的评论,并可以每隔一段时间获取最新的评论,也就是说我们希望我们的CommentBox可以动态地改变状态。
var CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, onCommentSubmit: function(comment) { data.push(comment); var self = this; setTimeout(function() { // 动态更新state self.setState({data: data}); }, 500); }, // 当组件render完成后自动被调用 componentDidMount: function() { var self = this; setTimeout(function() { // 动态更新state self.setState({data: data}); }, 2000); }, render:function(){ return ( <div className ="commentBox"> <h1>评论列表</h1> <CommentList data={this.props.data}/> <CommentForm onCommentSubmit={this.onCommentSubmit} /> </div> ) } });
以上是静态加载评论区,这个的react代码如下
var data = [ {author: "Pete Hunt", text: "This is one comment"}, {author: "Jordan Walke", text: "This is *another* comment"} ]; var CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, onCommentSubmit: function(comment) { data.push(comment); var self = this; setTimeout(function() { // 动态更新state self.setState({data: data}); }, 500); }, // 当组件render完成后自动被调用 componentDidMount: function() { var self = this; setTimeout(function() { // 动态更新state self.setState({data: data}); }, 2000); }, render:function(){ return ( <div className ="commentBox"> <h1>评论列表</h1> <CommentList data={this.props.data}/> <CommentForm onCommentSubmit={this.onCommentSubmit} /> </div> ) } }); var CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function(comment) { return ( <Comment author={comment.author} key={comment.id}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); } }); var CommentForm = React.createClass({ getInitialState: function() { return {author: '', text: ''}; }, handleAuthorChange: function(e) { this.setState({author: e.target.value}); }, handleTextChange: function(e) { this.setState({text: e.target.value}); }, handleSubmit: function(e) { e.preventDefault(); var author = this.state.author.trim(); var text = this.state.text.trim(); if (!text || !author) { return; } this.props.onCommentSubmit({author: author, text: text}); // TODO: send request to the server this.setState({author: '', text: ''});//清空文本框里内容 }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" value={this.state.author} onChange={this.handleAuthorChange}/> <input type="text" placeholder="Say something..." value={this.state.text} onChange={this.handleTextChange} /> <input type="submit" value="Post" /> </form> ); } }); var Comment =React.createClass({ rawMarkup: function() { var md = new Remarkable(); var rawMarkup = md.render(this.props.children.toString()); return { __html: rawMarkup }; }, render:function(){ return( <div className="comment"> <h2>{this.props.author}</h2> <span dangerouslySetInnerHTML={this.rawMarkup()} /> </div> ) } }); ReactDOM.render( <CommentBox data={data}/>, document.getElementById('content') );
原文章:http://www.open-open.com/lib/view/open1424570502236.html