(本文未审核)

目前为止,第二阶段知识已经基本介绍完,我们已经具备了项目上手实战必备的 React.js 知识,现在可以把这些知识应用起来。接下来是实战环节,我们会继续上一阶段的例子,把评论功能做得更加复杂一点。

我们在上一阶段的评论功能基础上加上以下功能需求:

  1. 页面加载完成自动聚焦到评论输入框。
  2. 把用户名持久化,存放到浏览器的 LocalStorage 中。页面加载时会把用户名加载出来显示到输入框,用户就不需要重新输入用户名了。
  3. 把已经发布的评论持久化,存放到浏览器的 LocalStorage 中。页面加载时会把已经保存的评论加载出来,显示到页面的评论列表上。
  4. 评论显示发布日期,如“1 秒前”,”30 分钟前”,并且会每隔 5 秒更新发布日期。
  5. 评论可以被删除。
  6. 类似 Markdown 的行内代码块显示功能,用户输入的用 `` 包含起来的内容都会被处理成用 <code> 元素包含。例如输入 `console.log` 就会处理成 <code>console.log</code> 再显示到页面上。

在线演示地址

大家可以在原来的第一阶段代码的基础上进行修改,第一、二阶段评论功能代码可以在这里找到: react-naive-book-examples。可以直接使用最新的样式文件 index.css 覆盖原来的 index.css。

接下来可以分析如何利用第二阶段的知识来构建这些功能,在这个过程里面可能会穿插一些小技巧,希望对大家有用。我们回顾一下这个页面的组成:

我们之前把页面分成了四种不同的组件:分别是 CommentApp 、CommentInputCommentListComment。我们开始修改这个组件,把上面的需求逐个完成。

自动聚焦到评论框

这个功能是很简单的,我们需要获取 textarea 的 DOM 元素然后调用 focus() API 就可以了。我们给输入框元素加上 ref 以便获取到 DOM 元素,修改 src/CommentInput.js 文件:

...
<textarea
ref={(textarea) => this.textarea = textarea}
value={this.state.content}
onChange={this.handleContentChange.bind(this)} />
...

组件挂载完以后完成以后就可以调用 this.textarea.focus(),给 CommentInput 组件加上 ComponentDidMount 生命周期:

class CommentInput extends Component {
static propTypes = {
onSubmit: PropTypes.func
} constructor () {
super()
this.state = {
username: '',
content: ''
}
} componentDidMount () {
this.textarea.focus()
}
...

这个功能就完成了。现在体验还不是很好,接下来我们把用户名持久化一下,体验就会好很多。

大家可以注意到我们给原来的 props.onSubmit 参数加了组件参数验证,在这次实战案例中,我们都会给评论功能的组件加上 propTypes 进行参数验证,接下来就不累述。

持久化用户名

用户输入用户名,然后我们把用户名保存到浏览器的 LocalStorage 当中,当页面加载的时候再从 LocalStorage 把之前保存的用户名显示到用户名输入框当中。这样用户就不用每次都输入用户名了,并且评论框是自动聚焦的,用户的输入体验就好很多。

我们监听用户名输入框失去焦点的事件 onBlur

...
<input
value={this.state.username}
onBlur={this.handleUsernameBlur.bind(this)}
onChange={this.handleUsernameChange.bind(this)} />
...

在 handleUsernameBlur 中我们把用户的输入内容保存到 LocalStorage 当中:

class CommentInput extends Component {
constructor () {
super()
this.state = {
username: '',
content: ''
}
} componentDidMount () {
this.textarea.focus()
} _saveUsername (username) {
localStorage.setItem('username', username)
} handleUsernameBlur (event) {
this._saveUsername(event.target.value)
}
...

在 handleUsernameBlur 中我们把用户输入的内容传给了 _saveUsername 私有方法(所有私有方法都以 _ 开头)。_saveUsername 会设置 LocalStorage 中的 username 字段,用户名就持久化了。这样就相当于每当用户输入完用户名以后(输入框失去焦点的时候),都会把用户名自动保存一次。

输入用户名,然后到浏览器里里面看看是否保存了:

然后我们组件挂载的时候把用户名加载出来。这是一种数据加载操作,我们说过,不依赖 DOM 操作的组件启动的操作都可以放在 componentWillMount 中进行,所以给 CommentInput 添加 componentWillMount 的组件生命周期:

...
componentWillMount () {
this._loadUsername()
} _loadUsername () {
const username = localStorage.getItem('username')
if (username) {
this.setState({ username })
}
} _saveUsername (username) {
localStorage.setItem('username', username)
}
...

componentWillMount 会调用 _loadUsername 私有方法,_loadUsername 会从 LocalStorage 加载用户名并且 setState 到组件的 state.username 中。那么组件在渲染的时候(render 方法)挂载的时候就可以用上用户名了。

这样体验就好多了,刷新页面,不需要输入用户名,并且自动聚焦到了输入框。我们 1、 2 需求都已经完成。

小贴士

这里插入一些小贴示,大家可以注意到我们组件的命名和方法的摆放顺序其实有一定的讲究,这里可以简单分享一下个人的习惯,仅供参考。

组件的私有方法都用 _ 开头,所有事件监听的方法都用 handle 开头。把事件监听方法传给组件的时候,属性名用 on 开头。例如:

<CommentInput
onSubmit={this.handleSubmitComment.bind(this)} />

这样统一规范处理事件命名会给我们带来语义化组件的好处,监听(onCommentInput 的 Submit 事件,并且交给 this 去处理(handle)。这种规范在多人协作的时候也会非常方便。

另外,组件的内容编写顺序如下:

  1. static 开头的类属性,如 defaultPropspropTypes
  2. 构造函数,constructor
  3. getter/setter(还不了解的同学可以暂时忽略)。
  4. 组件生命周期。
  5. _ 开头的私有方法。
  6. 事件监听方法,handle*
  7. render*开头的方法,有时候 render() 方法里面的内容会分开到不同函数里面进行,这些函数都以 render* 开头。
  8. render() 方法。

如果所有的组件都按这种顺序来编写,那么维护起来就会方便很多,多人协作的时候别人理解代码也会一目了然。


因为第三方评论工具有问题,对本章节有任何疑问的朋友可以移步到 React.js 小书的论坛 发帖,我会回答大家的疑问。

React.js 小书 Lesson25 - 实战分析:评论功能(四)的更多相关文章

  1. React.js 小书 Lesson16 - 实战分析:评论功能(三)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson16 转载请注明出处,保留原文链接和作者信息. 接下来的代码比较顺理成章了.修改 Commen ...

  2. React.js 小书 Lesson14 - 实战分析:评论功能(一)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson14 转载请注明出处,保留原文链接和作者信息. 课程到这里大家已经掌握了 React.js 的 ...

  3. React.js 小书 Lesson26 - 实战分析:评论功能(五)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson26 转载请注明出处,保留原文链接和作者信息. (本文未审核) 持久化评论 同样地,可以通过类 ...

  4. React.js 小书 Lesson15 - 实战分析:评论功能(二)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson15 转载请注明出处,保留原文链接和作者信息. 上一节我们构建了基本的代码框架,现在开始完善其 ...

  5. React.js 小书 Lesson27 - 实战分析:评论功能(六)

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson27 转载请注明出处,保留原文链接和作者信息. (本文未审核) 删除评论 现在发布评论,评论不 ...

  6. 【React.js小书】动手实现 React-redux(五):Provider - 方志

    我们要把 context 相关的代码从所有业务组件中清除出去,现在的代码里面还有一个地方是被污染的.那就是 src/index.js 里面的 Index: 1234567891011121314151 ...

  7. React.js 小书介绍

    React.js 小书 Github 关于作者 这是一本关于 React.js 的小书. 因为工作中一直在使用 React.js,也一直以来想总结一下自己关于 React.js 的一些知识.经验.于是 ...

  8. React.js 小书 Lesson20 - 更新阶段的组件生命周期

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson20 转载请注明出处,保留原文链接和作者信息. 从之前的章节我们了解到,组件的挂载指的是将组件 ...

  9. React.js 小书 Lesson24 - PropTypes 和组件参数验证

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson24 转载请注明出处,保留原文链接和作者信息. 我们来了到了一个非常尴尬的章节,很多初学的朋友 ...

随机推荐

  1. Partition--分区总结

    1. 在SQL SERVER 2008 R2 SP2之前版本,对分区只支持到1000个分区,之后版本支持到15000个分区.2. 分区索引对齐并不要求索引和表使用同一分区方案,但要求两者使用的分区方案 ...

  2. 只是误以为导入了maven依赖

    背景: 之前用Spring Boot 开发了一个小项目,考虑将代码迁到Git服务器,由于之前没用过Git,在将代码正式签入Git服务器前, 我想先签入一个最简单的Spring Boot程序代码作为试验 ...

  3. leetcode 字符串中的第一个唯一字符

    给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. 案例: s = "leetcode" 返回 0. s = "loveleetcod ...

  4. adt-bundle-windows不显示ADK Manage和其它图标的解决方法?

    我今天下载了包含ADT的eclipse,运行后发现在工具栏中居然没有ADK Manage和其它Android相关图标,这是为什么啊?上网搜索了一下,最终解决了!解决方法,把ADK的tool路径加入到p ...

  5. 模块time, datetime的用法

    一. time  time库提供了各种操作时间的方法   1. time.asctime([tuple]):将一个时间元组转换成一个可读的24个时间字符串. >>> time.asc ...

  6. Orleans MultiClient 多个Silo复合客户端

    目录 介绍 使用 简单例子 配置 注入到 DI 容器 添加多个 Client 全局 Orleans 服务配置 介绍 Orleans.MultiClient 是一个 Orleans 复合客户端,只需要简 ...

  7. 976 AlvinZH想回家(背包DP大作战T)

    976 AlvinZH想回家 思路 如果在第i小时有一些飞机延误,那么一架飞机的c值越大,这一小时产生的损失也越大.而使这一小时产生的损失尽可能的小并不会导致接下来时间产生的损失增大.因此应当每一小时 ...

  8. NavigatorIOS

    使用 NavigatorIOS,我们需要给他指定一个路由,这样它才能知道显示哪个页面 实例化 Navigator 需要2个必要的属性 —— initialRoute 和 renderSence,它们的 ...

  9. Log中关于start meeting在zVideoApp和zVideoUI中的流程可以搜索的几个字符串

    [31356:36164:04-29/17:53:38.164:INFO:SBConfUI.cpp(940)] CSBConfUI::OnConfStatusChanged(CONF_STATUS) ...

  10. train loss与test loss结果分析

    train loss 不断下降,test loss不断下降,说明网络仍在学习; train loss 不断下降,test loss趋于不变,说明网络过拟合; train loss 趋于不变,test ...