React.js基础篇(实战)——评论功能
Posted 小古时光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React.js基础篇(实战)——评论功能相关的知识,希望对你有一定的参考价值。
React.js的基础知识和组件的基本写法大多数已经讲完,现在可以把所学到的内容应用于实战当中,案例是一个评论功能,效果如下:
接下来会带大家一起来学习如何分析、编写这个功能。
React.js中一切都是组件,用Reac.js构建的功能其实也就是由各种组件组合而成,所以拿到一个需求以后,我们要做的第一件事就是理解需求、分析需求、划分这个需求有哪些组件构成。
组件的划分没有特别明确的标准,划分组件的目的性是为了代码可复用性、可维护性。只要某个部分有可能复用到别的地方,你都可以把它抽离出来当成一个组件;或者把某一部分抽离出来对代码的组织和管理会带来帮助,你也可以毫不犹豫地把它抽离出来。
对于上面这个评论功能,可以粗略地划分成以下几部分:
CommentApp:评论功能的整体组件,包含上部和下部两部分。
CommentInput:上面部分是负责用户输入可操作的输入区域,包括输入评论的用户名、评论内容和发布按钮。
CommentList:下面部分是评论列表,负责列表的展示。
Coomment:每个评论列表项由独立的组件Comment负责显示,这个组件被CommentList所使用。
所以这个评论功能划分成四种组件,CommentApp、CommentInput、CommentList、Comment。组件树表示如下:
用create-react-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
下面是评论列表,修改CommentInput.js中的内容:
import React,{Comment} from 'react'
class CommentInput extends Component{
render(){
return(
<div>CommentInput</div>
)
}
}
export default CommentInput
这里暂时让它简单返回<div>结构,同样地修改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')
)
然后进入工程目录启动:
在浏览器中可以看到,基本的结构已经渲染到了页面上了:
现在想让这个结构在浏览器中居中显示,我们就要给CommentApp里面的div添加样式。修改CommentApp中的render方法,给它添加一个wrapper类名:
...
class CommentApp extends Component{
render(){
return(
<div className='wrapper'>
<CommentInput />
<CommentList />
</div>
)
}
}
...
然后在index.css文件中添加样式:
.wrapper{
width: 500px;
margin: 10px auto;
font-size: 14px;
background-color: #fff;
border: 1px solid #f1f1f1;
padding: 20px;
}
在浏览器中可以看到样式生效了:
由于我们专注点在于Reac.js,这里写好了一个样式文件index.css,后续只需要在元素上加上类名就可以了:
body{
margin: 0;
padding: 0;
font-family: sans-serif;
background-color: #fbfbfb;
}
.wrapper{
width: 500px;
margin: 10px auto;
font-size: 14px;
background-color: #fff;
border: 1px solid #f1f1f1;
padding: 20px;
}
/* 评论框样式 */
.comment-input{
background-color: #fff;
border: 1px solid #f1f1f1;
padding: 20px;
margin-bottom: 10px;
}
.comment-field{
margin-bottom: 15px;
display: flex;
}
.comment-field .comment-field-name{
display: flex;
flex-basis: 100px;
font-size: 14px;
}
.comment-field .comment-field-input{
display: flex;
flex: 1;
}
.comment-field-input input,
.comment-field-input textarea{
border: 1px solid #e6e6e6;
border-radius: 3px;
padding: 5px;
outline: none;
font-size: 14px;
resize: none;
flex: 1;
}
.comment-field-input textarea{
height: 100px;
}
.comment-field-button{
display: flex;
justify-content: flex-end;
}
.comment-field-button button{
padding: 5px 10px;
width: 80px;
border: none;
border-radius: 3px;
background-color: #00a3cf;
color: #fff;
outline: none;
cursor: pointer;
}
.comment-field-button button:active{
background: #13c1f1;
}
/* 评论列表样式 */
.comment-list{
background-color: #fff;
border: 1px solid #f1f1f1;
padding: 20px;
}
/* 评论组件样式 */
.comment{
display: flex;
border-bottom: 1px solid #f1f1f1;
margin-bottom: 10px;
padding-bottom: 10px;
min-height: 50px;
}
.comment .comment-user{
flex-shrink: 0;
}
.comment .span{
color: #00a3cf;
font-style: italic;
}
.comment p{
margin: 0;
/*text-indent: 2em;*/
}
从CommentInput组件开始,学习React.js是如何处理用户输入的:
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
在浏览器中可以看到CommentInput的结构和样式都已生效:
因为还没有加入处理逻辑,所以你输入内容,然后点击发布是不会有什么效果的。用户可以输入内容是一个是用户名(username),一个是评论内容(content),我们在组件的构造函数中初始化一个state来把保存这两个状态:
...
class CommentInput extends Component{
constructor(){
super()
this.state={
username:' ',
content:' '
}
}
...
}
...
然后给输入框设置value属性,让它们的value值等于this.state里面相应的值:
...
<div className='comment-fleld'>
<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)。
同样地,让<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进行渲染。
父组件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。这样CommentList就可以调用this.props.onSubmit(...)把数据传给CommentApp。
现在在CommentInput中输入完评论内容以后点击发布,就可以看到CommentApp在控制台打印的数据:
接下来的代码比较顺理成章了,修改CommentList可以让它显示评论列表:
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的数据渲染到页面上,效果如下:
修改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
把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} />)}
</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,我们给Comment List加上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
})
}
...
https://coding.net/s/eadd6a28-e1ab-4f7d-9aa5-d0923a249afc
欢迎添加好友交流学习
▼ 猫来了点赞
以上是关于React.js基础篇(实战)——评论功能的主要内容,如果未能解决你的问题,请参考以下文章
vue.js项目实战运用篇之抖音视频APP-第十节: 评论列表功能
vue.js项目实战运用篇之抖音视频APP-第十节: 评论列表功能