今天继续来学习一下React,使用React实现B站评论区,如下图:
在使用React开发类似B站评论区的功能时,我们需要考虑以下几个关键点来构建一个基本的评论系统:
1. 设计组件结构
首先,设计组件结构是关键。至少需要以下几种组件:
- CommentList: 负责展示评论列表。
- Comment: 单个评论项的展示,包括用户名、评论内容、时间戳等。
- CommentContent: 用户输入评论的内容,包括文本输入框和提交按钮。
- User: 当前的登录的用户
- TabList: 评论排序的导航类型
2. 管理状态
使用React的状态管理功能(如useState或useReducer)来处理评论数据和表单输入状态。例如,可以创建一个状态来存储所有的评论数据,以及一个状态来管理评论表单的输入值。
3. 发送和获取评论
当然,本次实现不考虑连接后端,只利用假的数据来实现B站评论区的按照热度、事件排序展示、删除评论、发布评论。如果需要连接后端可以考虑下面你的操作:
获取评论: 如果评论数据来自后端API,可以使用fetch或第三方库如axios来获取数据,并在组件挂载时或根据需要触发请求。
发送评论: 当用户提交评论时,捕获表单数据并发送POST请求到后端API,成功后通常会刷新评论列表以显示新评论。
删除评论:当用户删除评论的时候,捕获评论Id并发送DELETE请求到后端API,后端根据ID删除该条评论。
4. 代码实现
import './App.scss'
import {useState} from "react";
import avatar1 from './images/1.png';
import avatar2 from './images/2.png';
import stukk from './images/lyy.jpg';
/**
* 评论列表的渲染和操作
*
* 1. 根据状态渲染评论列表
* 2. 删除评论
* 3. 发布评论
*/
// 评论列表数据
const defaultList = [
{
// 评论id
rpid: 3,
// 用户信息
user: {
uid: '13258165',
avatar: avatar1,
uname: '周杰伦',
},
// 评论内容
content: '哎哟,不错哦',
// 评论时间
ctime: '10-18 08:15',
like: 98,
},
{
rpid: 2,
user: {
uid: '36080105',
avatar: avatar2,
uname: '许嵩',
},
content: '我寻你千百度 日出到迟暮',
ctime: '11-13 11:29',
like: 88,
},
{
rpid: 1,
user: {
uid: '30009257',
avatar: stukk,
uname: 'stu_kk',
},
content: '关注stu_kk',
ctime: '10-19 09:00',
like: 66,
},
]
// 当前登录用户信息
const user = {
// 用户id
uid: '30009257',
// 用户头像
avatar: stukk,
// 用户昵称
uname: 'stu_kk',
}
/**
* 导航 Tab 的渲染和操作
*
* 1. 渲染导航 Tab 和高亮
* 2. 评论列表排序
* 最热 => 喜欢数量降序
* 最新 => 创建时间降序
*/
// 导航 Tab 数组
const tabs = [
{type: 'hot', text: '最热', isActive: true},
{type: 'time', text: '最新', isActive: false},
]
const App = () => {
const [commentList, setCommentList] = useState(defaultList); // 评论列表
const [tabList, setTabList] = useState(tabs); // 导航 Tab 数组
const [commentContent, setCommentContent] = useState(''); // 评论内容
const changeTabList = (id) => { //改变导航Tab
const newTabList = tabList.map((item, index) => {
if (index === id) {
item.isActive = true;
} else {
item.isActive = false;
}
return item;
});
return newTabList;
}
const clickTab = (id) => { // 点击导航Tab
const newCommentList = id === 0 ? commentList.sort((a, b) => b.like - a.like) : commentList.sort((a, b) => (
b.ctime > a.ctime ? 1 : -1
));
console.log(newCommentList)
setCommentList(newCommentList);
setTabList(changeTabList(id));
}
const sendComment = () => { //发布评论
console.log(111)
if (commentContent.trim() === '') {
alert('评论内容不能为空');
return;
}
const newCommentList = [
{
rpid: commentList.length + 1,
user: user,
content: commentContent,
ctime: '12-19 09:00',
like: 0,
},
...commentList,
];
setCommentList(newCommentList);
setCommentContent('');
}
const deleteCommentById = (id) => { //根据Id删除自己的评论
setCommentList(commentList.filter((item) => item.rpid !== id))
}
return (
<div className="app">
{/* 导航 Tab */}
<div className="reply-navigation">
<ul className="nav-bar">
<li className="nav-title">
<span className="nav-title-text">评论</span>
{/* 评论数量 */}
<span className="total-reply">{10}</span>
</li>
<li className="nav-sort">
{/* 高亮类名: active */}
{tabList.map((item,index) => (
<span onClick={()=>clickTab(index)} className={item.isActive ? 'nav-item active' : 'nav-item'} key={item.type}>{item.text}</span>
))}
</li>
</ul>
</div>
<div className="reply-wrap">
{/* 发表评论 */}
<div className="box-normal">
{/* 当前用户头像 */}
<div className="reply-box-avatar">
<div className="bili-avatar">
<img className="bili-avatar-img" src={user.avatar} alt="用户头像"/>
</div>
</div>
<div className="reply-box-wrap">
{/* 评论框 */}
<textarea
className="reply-box-textarea"
placeholder="发一条友善的评论"
value={commentContent}
onChange={(e) => setCommentContent(e.target.value)}
/>
{/* 发布按钮 */}
<div className="reply-box-send" onClick={sendComment}>
<div className="send-text" >发布</div>
</div>
</div>
</div>
{/* 评论列表 */}
<div className="reply-list">
{/* 评论项 */}
{commentList.map(item => (
<div className="reply-item" key={item.rpid}>
{/* 头像 */}
<div className="root-reply-avatar">
<div className="bili-avatar">
<img
className="bili-avatar-img"
alt=""
src={item.user.avatar}
/>
</div>
</div>
<div className="content-wrap">
{/* 用户名 */}
<div className="user-info">
<div className="user-name">{item.user.uname}</div>
</div>
{/* 评论内容 */}
<div className="root-reply">
<span className="reply-content">{item.content}</span>
<div className="reply-info">
{/* 评论时间 */}
<span className="reply-time">{item.ctime}</span>
{/* 评论数量 */}
<span className="reply-time">点赞数:{item.like}</span>
{/*根据用户与当前用户对比,设置是否有权限删除*/}
{item.user.uid === user.uid && <span className="delete-btn" onClick={() => deleteCommentById(item.rpid)}>删除</span> }
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
)
}
export default App
5. 实现效果
6. 代码解释
上面React代码实现了一个基础的评论区功能,包含评论列表的渲染、导航Tab切换(用于排序)、发布评论、以及删除自己的评论功能。下面是对代码的详细解析:
1. 数据初始化
评论列表数据 (defaultList): 定义了初始的评论数据数组,每个评论对象包含了评论ID (rpid)、用户信息 (user)、评论内容 (content)、评论时间 (ctime) 和点赞数 (like)。
当前登录用户信息 (user): 包含用户ID、头像和用户名,用于识别和执行删除操作。
2. 状态管理
- 使用 useState Hook 来管理组件状态:
- commentList: 当前的评论列表。
- tabList: 导航Tab的状态,包括类型、文本和是否激活。
- commentContent: 用户在评论框中输入的内容。
3. 导航Tab功能
- tabs: 定义了两个导航Tab选项:“最热”和“最新”,每个Tab都有一个类型 (type)、文本 (text) 和激活状态 (isActive)。
- changeTabList: 函数用来改变Tab的激活状态。
- clickTab: 处理Tab点击事件,根据选择的Tab类型对评论列表进行排序,并更新Tab的激活状态。
4. 发布评论
sendComment: 用户点击“发布”按钮时调用此函数,检查评论内容是否为空,然后将新评论追加到评论列表的开头,并清空输入框。
5. 删除评论
deleteCommentById: 根据评论ID从评论列表中删除对应的评论,仅允许用户删除自己发布的评论。
6. UI渲染
- 导航Tab (reply-navigation): 渲染导航Tab,根据tabList的状态来决定哪个Tab被高亮显示。
- 评论输入框和发布按钮: 用户可以在此输入评论内容并发布。
- 评论列表 (reply-list): 根据commentList渲染评论项,每个评论项包含用户头像、用户名、评论内容、评论时间和删除按钮(如果当前登录用户是评论作者)。
7. 总结
文章使用React构建了一个仿照B站基本的前端评论系统,实现了动态加载、排序、添加和删除评论的基本功能。通过React的状态管理和事件处理机制,实现了交互式的用户体验。但是在实际应用中,还需考虑与后端接口的交互、错误处理、分页加载、以及更复杂的用户交互逻辑。