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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM