React实践

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)