React.js 小书 Lesson15 - 实战分析:评论功能(二)
- 作者:胡子大哈
- 原文链接:http://huziketang.com/books/react/lesson15
- 转载请注明出处,保留原文链接和作者信息。
上一节我们构建了基本的代码框架,现在开始完善其他的内容。
处理用户输入
我们从 ComponentInput 组件开始,学习 React.js 是如何处理用户输入的。首先修改 ComponentInput.js,完善 ComponentInput 的 render 函数中的 HTML 结构:
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 来保存这两个状态:
...
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.username,username 在 constructor 中被初始化为空字符串。即使用户在输入框里面尝试输入内容了,还是没有改变 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
})
}
...
在这个方法中,我们通过 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在控制台打印的数据:

这样就顺利地把数据传递给了父组件,接下来我们开始处理评论列表相关的逻辑。
因为第三方评论工具有问题,对本章节有任何疑问的朋友可以移步到 React.js 小书的论坛 发帖,我会回答大家的疑问。
React.js 小书 Lesson15 - 实战分析:评论功能(二)的更多相关文章
- React.js 小书 Lesson25 - 实战分析:评论功能(四)
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson25 转载请注明出处,保留原文链接和作者信息. (本文未审核) 目前为止,第二阶段知识已经基本 ...
- React.js 小书 Lesson16 - 实战分析:评论功能(三)
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson16 转载请注明出处,保留原文链接和作者信息. 接下来的代码比较顺理成章了.修改 Commen ...
- React.js 小书 Lesson14 - 实战分析:评论功能(一)
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson14 转载请注明出处,保留原文链接和作者信息. 课程到这里大家已经掌握了 React.js 的 ...
- React.js 小书 Lesson26 - 实战分析:评论功能(五)
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson26 转载请注明出处,保留原文链接和作者信息. (本文未审核) 持久化评论 同样地,可以通过类 ...
- React.js 小书 Lesson27 - 实战分析:评论功能(六)
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson27 转载请注明出处,保留原文链接和作者信息. (本文未审核) 删除评论 现在发布评论,评论不 ...
- React.js 小书 Lesson3 - 前端组件化(二):优化 DOM 操作
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson3 转载请注明出处,保留原文链接和作者信息. 看看上一节我们的代码,仔细留意一下 change ...
- 【React.js小书】动手实现 React-redux(五):Provider - 方志
我们要把 context 相关的代码从所有业务组件中清除出去,现在的代码里面还有一个地方是被污染的.那就是 src/index.js 里面的 Index: 1234567891011121314151 ...
- React.js 小书介绍
React.js 小书 Github 关于作者 这是一本关于 React.js 的小书. 因为工作中一直在使用 React.js,也一直以来想总结一下自己关于 React.js 的一些知识.经验.于是 ...
- React.js 小书 Lesson20 - 更新阶段的组件生命周期
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson20 转载请注明出处,保留原文链接和作者信息. 从之前的章节我们了解到,组件的挂载指的是将组件 ...
随机推荐
- 使用VS Code编写Markdown文件
VS Code默认支持Markdown文件文件格式,这里介绍两个比较实用的功能,后续有新发现,可以持续更新. 实时预览 顾名思义,实时编辑,实时预览解析效果. 在VS Code扩展中搜索"M ...
- 最小生成树(kruscal算法)
其实kruscal算法很简单,把边从小到大排一遍,如果加入此边形成环,就不加,知道这棵树有n-1条边. 代码如下(一定要理解): #include<iostream> #include&l ...
- Mybatis 的动态 SQL 语句
<if>标签 我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询. 比如在 id 如果不为空时可以根据 id 查询, 如果 username 不同空时还要加入用户名作为条件.这种 ...
- 如何安装memcached
软件的下载,好像从官网上只能下载未经编译的源码,需要自己编译后才能安装使用,不熟悉的用户还是直接百度搜索下载比较好,这里也提供一个下载地址给大家参考. www.newasp.net/soft/6373 ...
- 【转载】Java资源大全中文版
Java资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容 ...
- luoguP4172 [WC2006]水管局长
https://www.luogu.org/problemnew/show/P4172 考虑倒序处理所有操作 先把不会被删掉的边加入图中,LCT 维护最小生成树,再倒序插入每一条边,如果边的 ( l, ...
- VS2017+DLib_19.17详细配置教程
最近学校布置了一个关于图像融合的作业,于是想利用Learn OpenCV 网站上的Face Morph 教程来设计一个人脸融合的Gif图,但是程序中需要用到DLib库,光是配置这个库就花费了我半天 ...
- 帝国cms刷新内容页提示.phome_ecms_news_data_' doesn't exist
帝国cms后台刷新提示.phome_ecms_news_data_' doesn't exist解决方法: 刷新所有信息内容页面时提示“Table '*.phome_ecms_article_data ...
- centos虚拟机设置静态ip
1.虚拟机网络配置设置为桥接模式 2.设置配置文件: vi /etc/sysconfig/network-scripts/ifcfg-ens33 DEVICE=ens33 HWADDR=:0c::c3 ...
- 洛谷 P1486 [NOI2004]郁闷的出纳员
题目描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资 ...