文章目录

一、创建项目

  1. 全局安装脚手架:​​npm i -g create-react-app​
  2. 使用脚手架创建项目:​​create-create-app my-react-demo​
  3. 切换到项目目录:​​cd my-react-demo​
  4. 运行项目:​​npm start​

二、项目实现

1. 整体效果

React 实现添加和删除评论功能(附完整代码)_创建项目

2. 需求分析

  • 可以输入并提交评论内容。
  • 评价内容展示和按热度或时间排序。
  • 删除评论列表中的评论。

3. 开发思路

  • App 组件
  • 添加评论组件 CommentAdd
  • 评论列表组件 CommentList
  • 评论Item项组件 CommentItem
  • 切换tab键组件 ChangeTab

4. 主要代码

① App.jsx 根组件

import './index.css'
import React from 'react'
import avatar from './images/avatar.png'
import PropTypes from 'prop-types'
import CommentAdd from './CommentAdd'
import CommentList from './CommentList'
import CommentItem from './CommentItem'
import ChangeTab from './ChangeTab'
import { toHaveAccessibleDescription } from '@testing-library/jest-dom/dist/matchers'

// 定义函数组件
function HelloFn(){
// 定义事件回调函数
const clickHandler = () => {
console.log('事件被触发了!');
}
return (
// 绑定事件
<button onClick={clickHandler}>这是我的第一个函数组件!click me!</button>
)
}
// 定义类组件
class HelloC extends React.Component {
// 定义事件回调函数
clickHandler = () => {
console.log('事件被触发了!');
}
render(){
return (
<button onClick={this.clickHandler}>这是我的第一个类组件!click me!</button>
)
}
}

class App extends React.Component {

// 依赖的数据
state = {
// hot: 热度排序 time: 时间排序
tabs: [
{
id: 1,
name: '热度',
type: 'hot'
},
{
id: 2,
name: '时间',
type: 'time'
}
],
active: 'hot',
comments: [
{
id: 1,
author: '刘德华',
comment: '给我一杯忘情水',
time: new Date('2021-10-10 09:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: 1
},
{
id: 2,
author: '周杰伦',
comment: '哎哟,不错哦',
time: new Date('2021-10-11 09:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: 0
},
{
id: 3,
author: '五月天',
comment: '不打扰,是我的温柔',
time: new Date('2021-10-11 10:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: -1
}
]
}
addComment = (comment) => {
// 将添加的评论追加到评论list上
const {comments} = this.state
comments.unshift(comment)
// 更新状态
this.setState({comments})
}
deleteComment = (index) => {
const {comments} = this.state
comments.splice(index,1)
this.setState({comments})
}
// 提供回调函数
changeCommentsSort = (newMsg) => {
this.setState({comments:newMsg})
}
render(){
return (
<div className="App">
{/* 渲染函数组件 */}
<HelloFn />
{/* 渲染类组件 */}
<HelloC />
<div className="comment-container">
{/* 评论数 */}
<div className="comment-head">
<span>{this.state.comments.length} 评论</span>
</div>
{/* 排序 */}
<div className="tabs-order">
{/* 子传父:① 首先需要父组件给子组件传一个回调函数,然后子组件调用 */}
<ChangeTab changeMsg={this.changeCommentsSort} comments={this.state.comments}/>
</div>
{/* 添加评论 */}
<CommentAdd addComment={this.addComment} />
{/* 评论列表 */}
<div className="comment-list">
<CommentList comments={this.state.comments} deleteComment={this.deleteComment} />
</div>
</div>
</div>
)}
}

export default

② 添加评论组件 CommentAdd.jsx

import React,{useRef} from 'react'
import PropTypes from 'prop-types'
import avatar from './images/avatar.png'
import { createRef } from 'react'
import {v4 as uuid} from 'uuid'

// 添加或删除评论 组件
class CommentAdd extends React.Component {
// 给组件对象指定state属性
// 初始化状态
constructor(props){
super(props)
this.state = {
username: '游客 第' + uuid() + '号',
content: '',
myDiv: createRef()
}
}

handleSubmit = () => {
const comment = {
author: this.state.username,
comment: this.state.myDiv.current.value,
time: new Date(),
attitude: 0
}
if(comment.comment != ''){
this.setState({username: '游客 第' + uuid() + '号'}) // 得到一个独一无二的uuid})
// 添加评论
this.props.addComment(comment)
// 清空输入数据
this.setState({author: ''})
this.state.myDiv.current.value = ''
} else {
alert('评论内容不能为空!')
}
}

render(){
const {comments} = this.state
return (
<div className="comment-send">
<div className="user-face">
<img className="user-head" src={avatar} alt="" />
</div>
<div className="textarea-container">
<textarea
cols="80"
rows="5"
placeholder="发条友善的评论"
className="ipt-txt"
ref={this.state.myDiv}
/>
<button className="comment-submit" onClick={this.handleSubmit}>发表评论</button>
</div>
<div className="comment-emoji">
<i className="face"></i>
<span className="text">表情</span>
</div>
</div>
)
}
}
export default

③ 评论列表组件 CommentList.jsx

import React, { Component } from "react";
import CommentItem from './CommentItem'

class CommentList extends Component {
render(){
const {comments,deleteComment} = this.props
const display = comments.length === 0 ? 'block' : 'none'
return (
<div className="list-item">
<h5 style={{display}}>暂无评论,点击添加评论!</h5>
<ul>{
comments.map((comment,index)=> <li key={index}><CommentItem
comment={comment}
index={index}
deleteComment={deleteComment} />
</li>)
}
</ul>
</div>
)
}
}
export default

④ 评论项组件 CommentItem.jsx

import React, { Component } from "react";
import avatar from './images/avatar.png'

class CommentItem extends Component {
handleDeleteComment = () => {
const { comment, deleteComment, index } = this.props
if (window.confirm(`确定删除${comment.author}的这条评论嘛?`)) {
deleteComment(index)
}
}
render() {
const { comment } = this.props
return (
<div className="list-item">
<div className="user-face">
<img className="user-head" src={avatar} alt="" />
</div>
<div className="comment" >
<div className="user">{comment.author}</div>
<p className="text">{comment.comment}</p>
<div className="info">
<span className="time">{comment.time.toLocaleString().replaceAll('/','-').replace('上午','').replace('下午','')}</span>
<span className="like liked">
<i className="icon" />
</span>
<span className="hate hated">
<i className="icon" />
</span>
<span className="reply btn-hover" onClick={this.handleDeleteComment}>删除</span>
</div>
</div>
</div>
)
}
}
export default

⑤ 切换tab组件 ChangeTab.jsx

import React from 'react'

// 切换tab 组件
class ChangeTab extends React.Component {
// 切换tab控件(按照时间、热度排序)
state = {
menuNum: 1
}
clickSortHandler = (num) => {
const {comments} = this.props
// 按热度排序(倒序)
if(num === 1){
comments.sort(this.compare('attitude',false))
this.props.changeMsg(comments)
}
// 按时间排序(倒序)
else if(num === 2) {
comments.sort(this.compare('time',false))
// ② 调用父组件传过来的回调函数,并注入参数(子传父)
this.props.changeMsg(comments)
}
this.setState({
menuNum: num
})
}
// 按某个字段进行排序
compare(prop,desc){
return function(a,b){
var value1 = a[prop]
var value2 = b[prop]
// 正序
if(desc == true){
return value1 - value2
}
// 倒序
else {
return value2 - value1
}
}
}
render(){
return (
<ul className="sort-container">
<li className={this.state.menuNum === 1 ? 'on' : ''} onClick={()=> this.clickSortHandler(1)}>按热度排序</li>
<li className={this.state.menuNum === 2 ? 'on' : ''} onClick={()=> this.clickSortHandler(2)}>按时间排序</li>
</ul>
)
}
}
export default

demo 完整代码已上传Github,欢迎 star ~

注: 此demo你可以学到:

  • React 项目中的组件化开发思想
  • 了解React项目的架构及组件拆分
  • 了解React组件传值及状态更新