React.js基础篇(实战)——评论功能

Posted 小古时光

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React.js基础篇(实战)——评论功能相关的知识,希望对你有一定的参考价值。


React.js基础篇(实战)——评论功能

React.js的基础知识和组件的基本写法大多数已经讲完,现在可以把所学到的内容应用于实战当中,案例是一个评论功能,效果如下:

React.js基础篇(实战)——评论功能

接下来会带大家一起来学习如何分析、编写这个功能。

组件的划分

React.js中一切都是组件,用Reac.js构建的功能其实也就是由各种组件组合而成,所以拿到一个需求以后,我们要做的第一件事就是理解需求、分析需求、划分这个需求有哪些组件构成。

组件的划分没有特别明确的标准,划分组件的目的性是为了代码可复用性、可维护性。只要某个部分有可能复用到别的地方,你都可以把它抽离出来当成一个组件;或者把某一部分抽离出来对代码的组织和管理会带来帮助,你也可以毫不犹豫地把它抽离出来。

对于上面这个评论功能,可以粗略地划分成以下几部分:

React.js基础篇(实战)——评论功能

CommentApp:评论功能的整体组件,包含上部和下部两部分。

CommentInput:上面部分是负责用户输入可操作的输入区域,包括输入评论的用户名、评论内容和发布按钮。

CommentList:下面部分是评论列表,负责列表的展示。

Coomment:每个评论列表项由独立的组件Comment负责显示,这个组件被CommentList所使用。

所以这个评论功能划分成四种组件,CommentApp、CommentInput、CommentList、Comment。组件树表示如下:

React.js基础篇(实战)——评论功能

组件实现

用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')

)

然后进入工程目录启动:

npm run start

在浏览器中可以看到,基本的结构已经渲染到了页面上了:

React.js基础篇(实战)——评论功能

添加样式

现在想让这个结构在浏览器中居中显示,我们就要给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;

}

在浏览器中可以看到样式生效了:

React.js基础篇(实战)——评论功能

由于我们专注点在于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的结构和样式都已生效:

React.js基础篇(实战)——评论功能

因为还没有加入处理逻辑,所以你输入内容,然后点击发布是不会有什么效果的。用户可以输入内容是一个是用户名(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值就会得到更新并显示到输入框内。这时候输入已经没有问题了:

React.js基础篇(实战)——评论功能

类似于<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在控制台打印的数据:

React.js基础篇(实战)——评论功能

接下来的代码比较顺理成章了,修改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的数据渲染到页面上,效果如下:

React.js基础篇(实战)——评论功能

修改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

可以看到测试数据显示到了页面上:

React.js基础篇(实战)——评论功能

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

这个时候可以看到浏览器报错了:

React.js基础篇(实战)——评论功能

这是因为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

    })

}

...

现在代码可以按照需求正常运作了:

React.js基础篇(实战)——评论功能

为了让代码的健壮性更强,给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基础篇(实战)——评论功能

欢迎添加好友交流学习



▼ 猫来了点赞

以上是关于React.js基础篇(实战)——评论功能的主要内容,如果未能解决你的问题,请参考以下文章

恶意代码分析实战——Lab03-01.exe基础动态分析篇

vue.js项目实战运用篇之抖音视频APP-第十节: 评论列表功能

vue.js项目实战运用篇之抖音视频APP-第十节: 评论列表功能

一篇包含了react所有基本点的文章

《nodejs+gulp+webpack基础实战篇》课程笔记--附加课

Vue.js基础篇实战--一个ToDoList小应用