基于React实现B站评论区

今天继续来学习一下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. 代码实现

javascript 复制代码
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的激活状态。
  1. 发布评论
    sendComment: 用户点击"发布"按钮时调用此函数,检查评论内容是否为空,然后将新评论追加到评论列表的开头,并清空输入框。

  2. 删除评论
    deleteCommentById: 根据评论ID从评论列表中删除对应的评论,仅允许用户删除自己发布的评论。

  3. UI渲染

  • 导航Tab (reply-navigation): 渲染导航Tab,根据tabList的状态来决定哪个Tab被高亮显示。
  • **评论输入框和发布按钮:**用户可以在此输入评论内容并发布。
  • **评论列表 (reply-list):**根据commentList渲染评论项,每个评论项包含用户头像、用户名、评论内容、评论时间和删除按钮(如果当前登录用户是评论作者)。

7. 总结

文章使用React构建了一个仿照B站基本的前端评论系统,实现了动态加载、排序、添加和删除评论的基本功能。通过React的状态管理和事件处理机制,实现了交互式的用户体验。但是在实际应用中,还需考虑与后端接口的交互、错误处理、分页加载、以及更复杂的用户交互逻辑。

相关推荐
Pedantic1 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘1 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆1 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
YFF菲菲兔2 小时前
调度系统和调和系统的桥梁
react.js
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen4 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端