实战分析:评论功能(二)

上一节咱们构建了基本的代码框架,如今开始完善其余的内容。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 在控制台打印的数据:

这样就顺利地把数据传递给了父组件,接下来咱们开始处理评论列表相关的逻辑。

相关文章
相关标签/搜索