react小书 笔记5

仅供个人学习!
本文作者:胡子大哈
本文原文:http://huziketang.com/books/react/blog/lesson4


组件实现

在写代码之前,我们先用 create-react-app 构建一个新的工程目录。所有的评论功能在这个工程内完成:

create-react-app comment-app

然后在工程目录下的 src/ 目录下新建四个文件,每个文件对应的是上述的四个组件。

src/
  CommentApp.js
  CommentInput.js
  CommentList.js
  Comment.js
  ...

你可以注意到,这里的文件名的开头是大写字母。我们遵循一个原则:如果一个文件导出的是一个类,那么这个文件名就用大写开头。四个组件类文件导出都是类,所以都是大写字母开头。

遵循“自顶而下,逐步求精”的原则,我们从组件的顶层开始,再一步步往下构建组件树。

先修改 CommentApp.js 如下:

import React, { Component } from 'react'
import CommentInput from './CommentInput'
import CommentList from './CommentList'

class CommentApp extends Component {
  render() {
    return (
      <div>
        <CommentInput />
        <CommentList />
      </div>
    )
  }
}

export default CommentApp

CommentApp 现在暂时还很简单,文件顶部引入了 CommentInput 和 CommentList 。然后按照上面的需求,应用在了 CommentApp 返回的 JSX 结构中,上面是用户输入区域,下面是评论列表。

修改 CommentInput.js 中的内容:

import React, { Component } from 'react'

class CommentInput extends Component {
  render() {
    return (
      <div>CommentInput</div>
    )
  }
}

export default CommentInput

同样地修改 CommentList.js :

import React, { Component } from 'react'

class CommentList extends Component {
  render() {
    return (
      <div>CommentList</div>
    )
  }
}

export default CommentList

现在可以把这个简单的结构渲染到页面上看看什么效果,修改 src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import CommentApp from './CommentApp'
import './index.css'

ReactDOM.render(
  <CommentApp />,
  document.getElementById('root')
)

然后进入工程目录启动工程:

扫描二维码关注公众号,回复: 3726462 查看本文章
npm run start

添加样式

现在想让这个结构在浏览器中居中显示,我们就要给 CommentApp 里面的 <div> 添加样式。

修改 CommentApp 中的render 方法,给它添加一个 wrapper 类名:

class CommentApp extends Component {
  render() {
    return (
      <div className='wrapper'>
        <CommentInput />
        <CommentList />
      </div>
    )
  }
}

 然后在 index.css 文件中添加样式:

这里写好了一个样式文件(https://github.com/huzidaha/react-naive-book-examples/blob/master/comment-app/src/index.css)提供给大家,可以复制到 index.css 当中。后续只需要在元素上加上类名就可以了。


用户可输入内容一个是用户名(username),一个是评论内容(content),我们在组件的构造函数中初始化一个 state 来保存这两个状态:

...
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 就不会变化。

被初始化为空字符串。即使用户在输入框里面尝试输入内容了,还是没有改变 this.state.username 是空字符串的事实。

应该怎么做才能把用户内容输入更新到输入框当中呢?

在 React.js 当中必须要用 setState 才能更新组件的内容,所以我们需要做的就是:监听输入框的 onChange 事件,然后获取到用户输入的内容,再通过 setState 的方式更新 state中的 username,这样 input 的内容才会更新。

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

上面的代码给 input 加上了 onChange 事件监听,绑定到 this.handleUsernameChange 方法中,该方法实现如下:

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

 Vue    event.target.value( ) 获取当前文本框的值(由事件触发时)

document.querySelector(CSS selectors) 选出一个类型的元素

 React.js å°ä¹¦å®æä¹è¯è®ºåè½å¾ç

所以当用户点击发布按钮的时候,我们就将 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 方法:

onsubmit 事件会在表单中的确认按钮被点击时发生。

onsubmit="SomeJavaScriptCode"

 修改 CommentApp.js ,让它可以通过传入回调来获取到新增评论数据:

class CommentApp extends Component {
  handleSubmitComment (comment) {
    console.log(comment)
  }

  render() {
    return (
      <div className='wrapper'>//包装,封皮
        <CommentInput
          onSubmit={this.handleSubmitComment.bind(this)} />//把commentinput打印的值传过来
        <CommentList />
      </div>
    )
  }
}

在 CommentApp 中给 CommentInput 传入一个 onSubmit 属性,这个属性值是 CommentApp 自己的一个方法 handleSubmitComment。这样 CommentInput 就可以调用 this.props.onSubmit(…) 把数据传给 CommenApp


修改 CommentList 可以让它可以显示评论列表:

// CommentList.js
import React, { Component } from 'react'

class CommentList extends Component {
  render() {
    const comments = [
      {username: 'Jerry', content: 'Hello'},
      {username: 'Tomy', content: 'World'},
      {username: 'Lucy', content: 'Good'}
    ]

    return (
      <div>{comments.map((comment, i) => {
        return (
          <div key={i}>
            {comment.username}:{comment.content}
          </div>
        )
      })}</div>
    )
  }
}

export default CommentList

建立了一个 comments 的数组来存放一些测试数据的内容,方便测试。然后把 comments 的数据渲染到页面上使用 map 构建一个存放 JSX 的数组。在浏览器看到效果:

修改 Comment.js 让它来负责具体每条评论内容的渲染:

import React, { Component } from 'react'

class Comment extends Component {
  render () {
    return (
      <div className='comment'>
        <div className='comment-user'>
          <span>{this.props.comment.username} </span>:
        </div>
        <p>{this.props.comment.content}</p>
      </div>
    )
  }
}

export default Comment

你只需要给它的 props 中传入一个 comment 对象,它就会把该对象中的 username 和 content 渲染到页面上。

把 Comment 应用到 CommentList 当中,修改 CommentList.js 代码:

import React, { Component } from 'react'
import Comment from './Comment' //新增

class CommentList extends Component {
  render() {
    const comments = [
      {username: 'Jerry', content: 'Hello'},
      {username: 'Tomy', content: 'World'},
      {username: 'Lucy', content: 'Good'}
    ]

    return (
      <div>
        {comments.map((comment, i) => <Comment comment={comment} key={i} />)} //change
      </div>
    )
  }
}

export default CommentList

CommentList 的数据应该是由父组件 CommentApp 传进来的,现在我们删除测试数据,改成从 props 获取评论数据:

import React, { Component } from 'react'
import Comment from './Comment'

class CommentList extends Component {
  render() {
    return (
      <div>
        {this.props.comments.map((comment, i) =>
          <Comment comment={comment} key={i} />
        )}
      </div>
    )
  }
}

export default CommentList

浏览器报错

这是因为CommentApp 使用 CommentList 的时候并没有传入 comments。我们给 CommentList 加上 defaultProps 防止 comments 不传入的情况:

class CommentList extends Component {
  static defaultProps = {
    comments: []
  }
...

这时候代码就不报错了。但是 CommentInput 给 CommentApp 传递的评论数据并没有传递给 CommentList

在 CommentApp 的 state 中初始化一个数组,来保存所有的评论数据,并且通过 props 把它传递给 CommentList。修改 CommentApp.js

import React, { Component } from 'react'
import CommentInput from './CommentInput'
import CommentList from './CommentList'

class CommentApp extends Component {
  constructor () {
    super()
    this.state = {
      comments: []
    }
  }

  handleSubmitComment (comment) {
    console.log(comment)
  }

  render() {
    return (
      <div className='wrapper'>
        <CommentInput onSubmit={this.handleSubmitComment.bind(this)} />
        <CommentList comments={this.state.comments}/>//传值给他
      </div>
    )
  }
}

export default CommentApp

修改 handleSubmitComment :每当用户发布评论的时候,就把评论数据插入 this.state.comments 中,然后通过 setState 把数据更新到页面上:

...
  handleSubmitComment (comment) {
    this.state.comments.push(comment)//添加一个或多个元素到数组的末尾
    this.setState({
      comments: this.state.comments
    })
  }
...

另外可以给 handleSubmitComment 加入简单的数据检查:

...
  handleSubmitComment (comment) {
    if (!comment) return
    if (!comment.username) return alert('请输入用户名')
    if (!comment.content) return alert('请输入评论内容')
    this.state.comments.push(comment)
    this.setState({
      comments: this.state.comments
    })
  }
...

组件之间(父子)用props传值

组件内部状态改变用setstate

猜你喜欢

转载自blog.csdn.net/qq_41775119/article/details/82822737