React.js 小書 Lesson27 - 實戰分析:評論功能(六)
轉載請注明出處,保留原文鏈接以及作者信息
在線閱讀:http://huziketang.com/books/react
{%raw%}
刪除評論
現在發布評論,評論不會消失,評論越來越多并不是什么好事。所以我們給評論組件加上刪除評論的功能,這樣就可以刪除不想要的評論了。修改 src/Comment.js
的 render
方法,新增一個刪除按鈕:
...
render () {
const { comment } = this.props
return (
<div className='comment'>
<div className='comment-user'>
<span className='comment-username'>
{comment.username}
</span>:
</div>
<p>{comment.content}</p>
<span className='comment-createdtime'>
{this.state.timeString}
</span>
<span className='comment-delete'>
刪除
</span>
</div>
)
}
...
我們在后面加了一個刪除按鈕,因為 index.css
定義了樣式,所以鼠標放到特定的評論上才會顯示刪除按鈕,讓用戶體驗好一些。
我們知道評論列表數據是放在 CommentApp
當中的,而這個刪除按鈕是在 Comment
當中的,現在我們要做的事情是用戶點擊某條評論的刪除按鈕,然后在 CommentApp
中把相應的數據刪除。但是 CommentApp
和 Comment
的關系是這樣的:

Comment
和 CommentApp
之間隔了一個 CommentList
,Comment
無法直接跟 CommentApp
打交道,只能通過 CommentList
來轉發這種刪除評論的消息。修改 Comment
組件,讓它可以把刪除的消息傳遞到上一層:
class Comment extends Component {
static propTypes = {
comment: PropTypes.object.isRequired,
onDeleteComment: PropTypes.func,
index: PropTypes.number
}
...
handleDeleteComment () {
if (this.props.onDeleteComment) {
this.props.onDeleteComment(this.props.index)
}
}
render () {
...
<span
onClick={this.handleDeleteComment.bind(this)}
className='comment-delete'>
刪除
</span>
</div>
)
}
現在在使用 Comment
的時候,可以傳入 onDeleteComment
和 index
兩個參數。index
用來標志這個評論在列表的下標,這樣點擊刪除按鈕的時候我們才能知道你點擊的是哪個評論,才能知道怎么從列表數據中刪除。用戶點擊刪除會調用 handleDeleteComment
,它會調用從上層傳入的 props. onDeleteComment
函數告知上一層組件刪除的消息,并且把評論下標傳出去。現在修改 src/CommentList.js
讓它把這兩個參數傳進來:
class CommentList extends Component {
static propTypes = {
comments: PropTypes.array,
onDeleteComment: PropTypes.func
}
static defaultProps = {
comments: []
}
handleDeleteComment (index) {
if (this.props.onDeleteComment) {
this.props.onDeleteComment(index)
}
}
render() {
return (
<div>
{this.props.comments.map((comment, i) =>
<Comment
comment={comment}
key={i}
index={i}
onDeleteComment={this.handleDeleteComment.bind(this)} />
)}
</div>
)
}
}
當用戶點擊按鈕的時候,Comment
組件會調用 props.onDeleteComment
,也就是 CommentList
的 handleDeleteComment
方法。而 handleDeleteComment
會調用 CommentList
所接受的配置參數中的 props.onDeleteComment
,并且把下標傳出去。
也就是說,我們可以在 CommentApp
給 CommentList
傳入一個 onDeleteComment
的配置參數來接受這個刪除評論的消息,修改 CommentApp.js
:
...
handleDeleteComment (index) {
console.log(index)
}
render() {
return (
<div className='wrapper'>
<CommentInput onSubmit={this.handleSubmitComment.bind(this)} />
<CommentList
comments={this.state.comments}
onDeleteComment={this.handleDeleteComment.bind(this)} />
</div>
)
}
}
...
現在點擊刪除按鈕,可以在控制臺看到評論對應的下標打印了出來。其實這就是這么一個過程:CommentList
把下標 index
傳給 Comment
。點擊刪除按鈕的時候,Comment
把 index
傳給了 CommentList
,CommentList
再把它傳給 CommentApp
。現在可以在 CommentApp
中刪除評論了:
...
handleDeleteComment (index) {
const comments = this.state.comments
comments.splice(index, 1)
this.setState({ comments })
this._saveComments(comments)
}
...
我們通過 comments.splice
刪除特定下標的評論,并且通過 setState
重新渲染整個評論列表;當然了,還需要把最新的評論列表數據更新到 LocalStorage 中,所以我們在刪除、更新以后調用了 _saveComments
方法把數據同步到 LocalStorage 中。
現在就可以愉快地刪除評論了。但是,你刪除評論以后 5 秒鐘后就會在控制臺中看到報錯了:

這是因為我們忘了清除評論的定時器,修改 src/Comment.js
,新增生命周期 commentWillUnmount
在評論組件銷毀的時候清除定時器:
...
componentWillUnmount () {
clearInterval(this._timer)
}
...
這才算完成了第 5 個需求。
顯示代碼塊
用戶在的輸入內容中任何以 `` 包含的內容都會用 <code>
包含起來顯示到頁面上。<code>
這是一個 HTML 結構,需要往頁面動態插入 HTML 結構我們只能用 dangerouslySetInnerHTML
了,修改 src/Comment.js
,把原來 render()
函數中的:
<p>{comment.content}</p>
修改成:
<p dangerouslySetInnerHTML={{
__html: this._getProcessedContent(comment.content)
}} />
我們把經過 this._getProcessedContent
處理的評論內容以 HTML 的方式插入到 <p>
元素中,this._getProcessedContent
是這樣實現的:
...
_getProcessedContent (content) {
return content
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
.replace(/`([\S\s]+?)`/g, '<code>$1</code>')
}
...
看起來很復雜,其實前 5 行是用來處理 HTML 內容轉義的,最后一行是用來插入 <code>
標簽的。如果我們把用戶輸入的內容全部以 HTML 顯示到頁面上,那么就會造成跨站腳本攻擊。所以前 5 個 replace
實際上是把類似于 <
、>
這種內容替換轉義一下。而最后一行才是真正實現需求的代碼,把 `` 包含的內容用 <code>
包裹起來。
輸入:
這是代碼塊 `console.log`,這是 <h1>正常內容</h1>。
看看效果:

我們安全地完成了第 6 個需求。到目前為止,第二階段的實戰已經全部完成,你可以在這里找到完整的代碼。
總結
到這里第二階段已經全部結束,我們已經掌握了全部 React.js 實戰需要的入門知識。接下來我們會學習兩個相對比較高級的 React.js 的概念,然后進入 React-router 和 Redux 的世界,讓它們配合 React.js 來構建更成熟的前端頁面。
{%endraw%}
下一節中我們將介紹《React.js 小書 Lesson28 - 高階組件(Higher-Order Components)》。