1. 需求
- 展示评论列表
- 实现删除功能
2.1 只有自己的评论才展示删除按钮
2.2 点击删除按钮,删除当前评论 - tab切换(点击对应tab,对tab文案高亮处理)
- 评论高亮
- 评论排序(最新、最热)
2. 实现思路
- useState维护评论列表
- map方法遍历渲染
- 删除显示——条件渲染
- 删除功能——拿到当前评论项的id,然后对原有评论列表进行过滤,根据id剔除要删除的评论项
- tab高亮——记录点击的tab的type,根据type去匹配高亮样式
- 评论排序——根据tab的type对评论列表状态数据进行不同的排序处理,当成新值传给set方法重新渲染视图UI(注意:排序后不要修改评论列表状态数据的原始值,教程里推荐使用lodash包的orderBy方法)
3. 代码
- 样式是我自己随便写的,具体可以参考b站本身的css样式
3.1 App.js
import { useState } from "react"
import _ from 'lodash'
import './App.css'
function App() {
// 评论列表
const initReviewList = [
{
rid: 2,
user: {
uid: '002',
avator: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.gC14cncyeUsZYKNwXOyBtgHaHa?rs=1&pid=ImgDetMain',
uname: '小猫爱吃鱼'
},
content: '喵喵喵',
ctime: '09-22 17:55',
like: 90
},
{
rid: 1,
user: {
uid: '001',
avator: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.qCys2C8LjF8c3_UHbGOooAAAAA?rs=1&pid=ImgDetMain',
uname: '小狗爱吃骨头'
},
content: '汪汪汪',
ctime: '09-22 14:55',
like: 100
},
{
rid: 0,
user: {
uid: '000',
avator: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.pL9aeO50HMujMSzGcOPhKwAAAA?rs=1&pid=ImgDetMain',
uname: '二郎神'
},
content: '666',
ctime: '09-30 12:23',
like: 88
}
]
// 当前登录的用户信息
const user = {
uid: '000',
avator: 'https://tse4-mm.cn.bing.net/th/id/OIP-C.pL9aeO50HMujMSzGcOPhKwAAAA?rs=1&pid=ImgDetMain',
uname: '二郎神'
}
// tab
const tabs = [
{
type: 'hot',
text:'最热',
},
{
type: 'new',
text:'最新',
}
]
const [reviewList, setReviewList] = useState(_.orderBy(initReviewList, 'like', 'desc'))
function handleDeleteReview(currentRid) {
setReviewList(reviewList.filter(item=> item.rid !== currentRid))
}
const [currTabType, steCurrTabType] = useState('hot')
function handleClickTab(type) {
steCurrTabType(type)
if (type === 'hot') {
// 根据点赞数量,降序排序
setReviewList(_.orderBy(reviewList, 'like', 'desc'))
} else {
// 根据评论时间,降序排序
setReviewList(_.orderBy(reviewList, 'ctime', 'desc'))
}
}
return (
<div className="App">
{/* 导航栏 */}
<div className="review-header">
{/* 标题 */}
<div className="review-title-container">
<span className="review-title">评论</span>
{/* 评论总数 */}
<div className="review-total">{reviewList.length ?? 0}</div>
</div>
<div className="review-tabs-container">
{tabs.map((item, index) => {
return <div key={item.type} className="tabs-item">
<span className={`tabs-text ${item.type === currTabType && 'tabs-active'}`} onClick={() => handleClickTab(item.type)}>{item.text}</span>
{index < tabs.length - 1 && <div className="tabs-idot">|</div>}
</div>
})}
</div>
</div>
{/* 评论 */}
<div className="review-wrap">
{/* 发表评论 */}
<div className="box-normal">
<img className="normal-avator" src={user.avator}></img>
<input className="review-input" placeholder="发一条友善的评论"></input>
<button className="add-review">发布</button>
</div>
{/* 评论列表 */}
<div className="review-list">
{reviewList.map((item) => {
{/* 每一条评论 */ }
return <div className="review-item" key={item.rid}>
{/* 头像 */}
<img className="review-avator" src={item.user.avator}></img>
<div className="review-item-content">
{/* 用户名 */}
<div className="user-name">{item.user.uname}</div>
{/* 评论内容 */}
<span className="review-content">{item.content}</span>
{/* 评论相关信息 */}
<div className="review-msg">
{/* 评论时间 */}
<span className="review-date">{item.ctime}</span>
{/* 点赞数量 */}
<span className="good-count">点赞数:{item.like}</span>
{/* 删除评论按钮 */}
{item.user.uid === user.uid && <span className="review-delete" onClick={() => handleDeleteReview(item.rid)}>删除</span>}
</div>
{/* 分割线 */}
<div className="part-line"></div>
</div>
</div>
})}
</div>
</div>
</div>
);
}
export default App;
3.2 App.css
.review-header {
display: flex;
.review-title-container {
display: flex;
align-items: center;
margin-right: 40px;
.review-title {
font-weight: bold;
margin-right: 2px;
}
.review-total {
font-size: 12px;
color: gray;
}
}
.review-tabs-container {
display: flex;
align-items: center;
font-size: 12px;
font-weight: bold;
.tabs-item {
color: gray;
display: flex;
.tabs-text {
cursor: pointer;
}
.tabs-active {
color: black;
}
.tabs-idot {
margin: 0px 5px;
}
}
}
}
.box-normal {
display: flex;
margin: 16px 0px 30px 0px;
.normal-avator {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 20px;
}
.review-input {
border: 1px solid #F1F2F3;
background-color: #F1F2F3;
border-radius: 6px;
padding-left: 10px;
}
.add-review {
background-color: skyblue;
border: transparent;
border-radius: 6px;
color: #fff;
margin-left: 6px;
padding: 0px 10px;
}
}
.review-list {
.review-item {
display: flex;
margin-bottom: 15px;
.review-avator {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 20px;
}
.review-item-content {
.user-name {
font-size: 12px;
font-weight: bold;
color: gray;
margin-bottom: 6px;
}
.review-content {
font-size: 14px;
}
.review-msg {
font-size: 10px;
color: gray;
.review-date {
margin-right: 15px;
}
.good-count {
margin-right: 15px;
}
.review-delete {
cursor: pointer;
}
}
.part-line {
margin-top: 5px;
height: 0.5px;
background-color: #E3E5E7;
}
}
}
}
3.3 效果图
参考
黑马程序员react教程