基于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的状态管理和事件处理机制,实现了交互式的用户体验。但是在实际应用中,还需考虑与后端接口的交互、错误处理、分页加载、以及更复杂的用户交互逻辑。

相关推荐
冰红茶-Tea8 分钟前
typescript数据类型(二)
前端·typescript
slongzhang_12 分钟前
elementPlus消息组件多按钮案例
前端·javascript·vue.js
会发光的猪。37 分钟前
vue中el-select选择框带搜索和输入,根据用户输入的值显示下拉列表
前端·javascript·vue.js·elementui
旺旺大力包1 小时前
【 Git 】git 的安装和使用
前端·笔记·git
雪落满地香1 小时前
前端:改变鼠标点击物体的颜色
前端
余生H2 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
outstanding木槿2 小时前
JS中for循环里的ajax请求不数据
前端·javascript·react.js·ajax
酥饼~2 小时前
html固定头和第一列简单例子
前端·javascript·html
一只不会编程的猫2 小时前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250932 小时前
html 通用错误页面
前端·html