React.js 小書 Lesson15 - 實(shí)戰(zhàn)分析:評論功能(二)
轉(zhuǎn)載請注明出處,保留原文鏈接以及作者信息
在線閱讀:http://huziketang.com/books/react
上一節(jié)我們構(gòu)建了基本的代碼框架,現(xiàn)在開始完善其他的內(nèi)容。
處理用戶輸入
我們從 ComponentInput 組件開始,學(xué)習(xí) React.js 是如何處理用戶輸入的。首先修改 ComponentInput.js,完善 ComponentInput 的 render 函數(shù)中的 HTML 結(jié)構(gòu):
import React, { Component } from 'react'
class CommentInput extends Component {
render () {
return (
<div className='comment-input'>
<div className='comment-field'>
<span className='comment-field-name'>用戶名:</span>
<div className='comment-field-input'>
<input />
</div>
</div>
<div className='comment-field'>
<span className='comment-field-name'>評論內(nèi)容:</span>
<div className='comment-field-input'>
<textarea />
</div>
</div>
<div className='comment-field-button'>
<button>
發(fā)布
</button>
</div>
</div>
)
}
}
export default CommentInput
在瀏覽器中可以看到 ComponentInput 的結(jié)構(gòu)和樣式都已經(jīng)生效:
[圖片上傳失敗...(image-131ff0-1510226601202)]
因?yàn)檫€沒有加入處理邏輯,所以你輸入內(nèi)容,然后點(diǎn)擊發(fā)布是不會有什么效果的。用戶可輸入內(nèi)容一個是用戶名(username),一個是評論內(nèi)容(content),我們在組件的構(gòu)造函數(shù)中初始化一個 state 來保存這兩個狀態(tài):
...
class CommentInput extends Component {
constructor () {
super()
this.state = {
username: '',
content: ''
}
}
...
}
...
然后給輸入框設(shè)置 value 屬性,讓它們的 value 值等于 this.state 里面相應(yīng)的值:
...
<div className='comment-field'>
<span className='comment-field-name'>用戶名:</span>
<div className='comment-field-input'>
<input value={this.state.username} />
</div>
</div>
<div className='comment-field'>
<span className='comment-field-name'>評論內(nèi)容:</span>
<div className='comment-field-input'>
<textarea value={this.state.content} />
</div>
</div>
...
可以看到接受用戶名輸入的 <input /> 和接受用戶評論內(nèi)容的 <textarea /> 的 value 值分別由 state.username 和 state.content 控制。這時候你到瀏覽器里面去輸入內(nèi)容看看,你會發(fā)現(xiàn)你什么都輸入不了。
這是為什么呢?React.js 認(rèn)為所有的狀態(tài)都應(yīng)該由 React.js 的 state 控制,只要類似于 <input />、<textarea />、<select /> 這樣的輸入控件被設(shè)置了 value 值,那么它們的值永遠(yuǎn)以被設(shè)置的值為準(zhǔn)。值不變,value 就不會變化。
例如,上面設(shè)置了 <input /> 的 value 為 this.state.username,username 在 constructor 中被初始化為空字符串。即使用戶在輸入框里面嘗試輸入內(nèi)容了,還是沒有改變 this.state.username 是空字符串的事實(shí)。
所以應(yīng)該怎么做才能把用戶內(nèi)容輸入更新到輸入框當(dāng)中呢?在 React.js 當(dāng)中必須要用 setState 才能更新組件的內(nèi)容,所以我們需要做的就是:監(jiān)聽輸入框的 onChange 事件,然后獲取到用戶輸入的內(nèi)容,再通過 setState 的方式更新 state 中的 username,這樣 input 的內(nèi)容才會更新。
...
<div className='comment-field-input'>
<input
value={this.state.username}
onChange={this.handleUsernameChange.bind(this)} />
</div>
...
上面的代碼給 input 加上了 onChange 事件監(jiān)聽,綁定到 this.handleUsernameChange 方法中,該方法實(shí)現(xiàn)如下:
...
handleUsernameChange (event) {
this.setState({
username: event.target.value
})
}
...
在這個方法中,我們通過 event.target.value 獲取 <input /> 中用戶輸入的內(nèi)容,然后通過 setState 把它設(shè)置到 state.username 當(dāng)中,這時候組件的內(nèi)容就會更新,input 的 value 值就會得到更新并顯示到輸入框內(nèi)。這時候輸入已經(jīng)沒有問題了:
[圖片上傳失敗...(image-b1ad5-1510226601202)]
類似于 <input />、<select />、<textarea> 這些元素的 value 值被 React.js 所控制、渲染的組件,在 React.js 當(dāng)中被稱為受控組件(Controlled Component)。對于用戶可輸入的控件,一般都可以讓它們成為受控組件,這是 React.js 所推崇的做法。另外還有非受控組件,這里暫時不提及。
同樣地,讓 <textarea /> 成為受控組件:
...
handleContentChange (event) {
this.setState({
content: event.target.value
})
}
...
<div className='comment-field'>
<span className='comment-field-name'>評論內(nèi)容:</span>
<div className='comment-field-input'>
<textarea
value={this.state.content}
onChange={this.handleContentChange.bind(this)} />
</div>
</div>
...
向父組件傳遞數(shù)據(jù)
當(dāng)用戶在 CommentInput 里面輸入完內(nèi)容以后,點(diǎn)擊發(fā)布,內(nèi)容其實(shí)是需要顯示到 CommentList 組件當(dāng)中的。但這兩個組件明顯是單獨(dú)的、分離的組件。我們再回顧一下之前是怎么劃分組件的:
[圖片上傳失敗...(image-cd9ac0-1510226601202)]
可以看到,CommentApp 組件將 CommentInput 和 CommentList 組合起來,它是它們倆的父組件,可以充當(dāng)橋接兩個子組件的橋梁。所以當(dāng)用戶點(diǎn)擊發(fā)布按鈕的時候,我們就將 CommentInput 的 state 當(dāng)中最新的評論數(shù)據(jù)傳遞給父組件 CommentApp ,然后讓父組件把這個數(shù)據(jù)傳遞給 CommentList 進(jìn)行渲染。
CommentInput 如何向 CommentApp 傳遞的數(shù)據(jù)?父組件 CommentApp 只需要通過 props 給子組件 CommentInput 傳入一個回調(diào)函數(shù)。當(dāng)用戶點(diǎn)擊發(fā)布按鈕的時候,CommentInput 調(diào)用 props 中的回調(diào)函數(shù)并且將 state 傳入該函數(shù)即可。
先給發(fā)布按鈕添加事件:
...
<div className='comment-field-button'>
<button
onClick={this.handleSubmit.bind(this)}>
發(fā)布
</button>
</div>
...
用戶點(diǎn)擊按鈕的時候會調(diào)用 this.handleSumit 方法:
...
handleSubmit () {
if (this.props.onSubmit) {
const { username, content } = this.state
this.props.onSubmit({username, content})
}
this.setState({ content: '' })
}
...
handleSumit 方法會判斷 props 中是否傳入了 onSubmit 屬性。有的話就調(diào)用該函數(shù),并且把用戶輸入的用戶名和評論數(shù)據(jù)傳入該函數(shù)。然后再通過 setState 清空用戶輸入的評論內(nèi)容(但為了用戶體驗(yàn),保留輸入的用戶名)。
修改 CommentApp.js ,讓它可以通過傳入回調(diào)來獲取到新增評論數(shù)據(jù):
class CommentApp extends Component {
handleSubmitComment (comment) {
console.log(comment)
}
render() {
return (
<div className='wrapper'>
<CommentInput
onSubmit={this.handleSubmitComment.bind(this)} />
<CommentList />
</div>
)
}
}
在 CommentApp 中給 CommentInput 傳入一個 onSubmit 屬性,這個屬性值是 CommentApp 自己的一個方法 handleSubmitComment。這樣 CommentInput 就可以調(diào)用 this.props.onSubmit(…) 把數(shù)據(jù)傳給 CommenApp。
現(xiàn)在在 CommentInput 中輸入完評論內(nèi)容以后點(diǎn)擊發(fā)布,就可以看到 CommentApp 在控制臺打印的數(shù)據(jù):
[圖片上傳失敗...(image-b81115-1510226601202)]
這樣就順利地把數(shù)據(jù)傳遞給了父組件,接下來我們開始處理評論列表相關(guān)的邏輯。
下一節(jié)中我們將介紹《React.js 小書 Lesson16 - 實(shí)戰(zhàn)分析:評論功能(三)》。