實戰分析:評論功能(二)

上一節咱們構建了基本的代碼框架,如今開始完善其餘的內容。html

處理用戶輸入

咱們從 ComponentInput 組件開始,學習 React.js 是如何處理用戶輸入的。首先修改 ComponentInput.js,完善 ComponentInput 的 render 函數中的 HTML 結構:react

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'>評論內容:</span>
          <div className='comment-field-input'>
            <textarea />
          </div>
        </div>
        <div className='comment-field-button'>
          <button>
            發佈
          </button>
        </div>
      </div>
    )
  }
}

export default CommentInput

在瀏覽器中能夠看到 ComponentInput 的結構和樣式都已經生效:瀏覽器

由於尚未加入處理邏輯,因此你輸入內容,而後點擊發布是不會有什麼效果的。用戶可輸入內容一個是用戶名(username),一個是評論內容(content),咱們在組件的構造函數中初始化一個 state 來保存這兩個狀態:app

...
class CommentInput extends Component {
  constructor () {
    super()
    this.state = {
      username: '',
      content: ''
    }
  }
  ...
}
...

而後給輸入框設置 value 屬性,讓它們的 value 值等於 this.state 裏面相應的值:框架

...
        <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'>評論內容:</span>
          <div className='comment-field-input'>
            <textarea value={this.state.content} />
          </div>
        </div>
...

能夠看到接受用戶名輸入的 <input /> 和接受用戶評論內容的 <textarea /> 的 value 值分別由 state.username 和 state.content 控制。這時候你到瀏覽器裏面去輸入內容看看,你會發現你什麼都輸入不了。函數

這是爲何呢?React.js 認爲全部的狀態都應該由 React.js 的 state 控制,只要相似於 <input /><textarea /><select /> 這樣的輸入控件被設置了 value值,那麼它們的值永遠以被設置的值爲準。值不變,value 就不會變化。學習

例如,上面設置了 <input /> 的 value 爲 this.state.usernameusername 在 constructor 中被初始化爲空字符串。即便用戶在輸入框裏面嘗試輸入內容了,仍是沒有改變 this.state.username 是空字符串的事實。this

因此應該怎麼作才能把用戶內容輸入更新到輸入框當中呢?在 React.js 當中必需要用 setState 才能更新組件的內容,因此咱們須要作的就是:監聽輸入框的 onChange 事件,而後獲取到用戶輸入的內容,再經過 setState 的方式更新 state中的 username,這樣 input 的內容纔會更新。spa

...
    <div className='comment-field-input'>
      <input
        value={this.state.username}
        onChange={this.handleUsernameChange.bind(this)} />
    </div>
...

上面的代碼給 input 加上了 onChange 事件監聽,綁定到 this.handleUsernameChange 方法中,該方法實現以下:3d

...
  handleUsernameChange (event) {
    this.setState({
      username: event.target.value
    })
  }
...

在這個方法中,咱們經過 event.target.value 獲取 <input /> 中用戶輸入的內容,而後經過 setState 把它設置到 state.username 當中,這時候組件的內容就會更新,input 的 value 值就會獲得更新並顯示到輸入框內。這時候輸入已經沒有問題了:

相似於 <input /><select /><textarea> 這些元素的 value 值被 React.js 所控制、渲染的組件,在 React.js 當中被稱爲受控組件(Controlled Component)。對於用戶可輸入的控件,通常均可以讓它們成爲受控組件,這是 React.js 所推崇的作法。另外還有非受控組件,這裏暫時不說起。

一樣地,讓 <textarea /> 成爲受控組件:

...
  handleContentChange (event) {
    this.setState({
      content: event.target.value
    })
  }
...
      <div className='comment-field'>
        <span className='comment-field-name'>評論內容:</span>
        <div className='comment-field-input'>
          <textarea
            value={this.state.content}
            onChange={this.handleContentChange.bind(this)} />
        </div>
      </div>
...

向父組件傳遞數據

當用戶在 CommentInput 裏面輸入完內容之後,點擊發布,內容實際上是須要顯示到 CommentList 組件當中的。但這兩個組件明顯是單獨的、分離的組件。咱們再回顧一下以前是怎麼劃分組件的:

能夠看到,CommentApp 組件將 CommentInput 和 CommentList 組合起來,它是它們倆的父組件,能夠充當橋接兩個子組件的橋樑。因此當用戶點擊發布按鈕的時候,咱們就將 CommentInput 的 state 當中最新的評論數據傳遞給父組件 CommentApp ,而後讓父組件把這個數據傳遞給 CommentList 進行渲染。

CommentInput 如何向 CommentApp 傳遞的數據?父組件 CommentApp 只須要經過 props 給子組件 CommentInput 傳入一個回調函數。當用戶點擊發布按鈕的時候,CommentInput 調用 props 中的回調函數而且將 state 傳入該函數便可。

先給發佈按鈕添加事件:

...
      <div className='comment-field-button'>
        <button
          onClick={this.handleSubmit.bind(this)}>
          發佈
        </button>
      </div>
...

用戶點擊按鈕的時候會調用 this.handleSubmit 方法:

...
  handleSubmit () {
    if (this.props.onSubmit) {
      const { username, content } = this.state
      this.props.onSubmit({username, content})
    }
    this.setState({ content: '' })
  }
...

handleSubmit 方法會判斷 props 中是否傳入了 onSubmit 屬性。有的話就調用該函數,而且把用戶輸入的用戶名和評論數據傳入該函數。而後再經過 setState 清空用戶輸入的評論內容(但爲了用戶體驗,保留輸入的用戶名)。

修改 CommentApp.js ,讓它能夠經過傳入回調來獲取到新增評論數據:

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 就能夠調用 this.props.onSubmit(…) 把數據傳給 CommenApp

如今在 CommentInput 中輸入完評論內容之後點擊發布,就能夠看到 CommentApp 在控制檯打印的數據:

這樣就順利地把數據傳遞給了父組件,接下來咱們開始處理評論列表相關的邏輯。

相關文章
相關標籤/搜索