天天看點

利用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/[email protected]/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