主页 > 开源代码  > 

React:B站评论demo,实现列表渲染、删除按钮显示和功能实现、导航栏渲染切换及高亮显示、评论区的排序

React:B站评论demo,实现列表渲染、删除按钮显示和功能实现、导航栏渲染切换及高亮显示、评论区的排序
功能要求:

1、渲染评论列表

2、删除评论功能:只显示自己评论的删除按钮;点击删除按钮,删除当前评论,列表中不再显示。

3、渲染导航Tab(最新 | 最热)和其 高亮实现

4、评论排序功能实现(最新:按时间排序 | 最热:按点赞数排序)

核心思路:

1、使用map方法对列表数据进行遍历渲染(别忘记加key)

2、使用useState维护评论列表

3、只显示自己评论删除按钮,应当是一个条件渲染

4、删除功能:点击删除按钮时,传出唯一匹配项 id,以id为条件对评论列表做 filter过滤。判断当前登录用户信息当中的uid 和 评论数据当中的uid 一样时,才显示这条评论的删除按钮(user.id === item.user.id)

5、Tab功能实现:点击谁就把谁的 type(独一无二的标识)记录下来,然后和遍历时的每一项的type 做匹配,谁匹配到就设置负责高亮的类名。

6、评论区按序排列,用到了lodash库的排序函数

源码 以及 详细代码注释 如下:  // App.js import './App.scss' import jayAvatar from './images/jay.png' import ljAvatar from './images/lj.png' import xsAvatar from './images/xs.png' import React, { useState } from 'react' import _ from 'lodash' // 评论列表的渲染和操作 1. 根据状态渲染评论列表 2. 删除评论 // 评论列表数据 const defaultList = [ { // 评论id rpid: 3, // 用户信息 user: { uid: '13258165', avatar: jayAvatar, uname: '周杰伦', }, // 评论内容 content: '哎哟,不错哦', // 评论时间 ctime: '10-18 08:15', like: 989, }, { rpid: 2, user: { uid: '36080105', avatar: xsAvatar, uname: '许嵩', }, content: '我寻你千百度 日出到迟暮', ctime: '11-13 11:29', like: 88, }, { rpid: 1, user: { uid: '30009257', avatar: ljAvatar, uname: '李健', }, content: '只是因为在人群中多看了你一眼', ctime: '10-19 09:00', like: 66, }, ] // 当前登录用户信息 const user = { // 用户id uid: '30009257', // 用户头像 avatar: jayAvatar, // 用户昵称 uname: '李健', } // 导航 Tab 的渲染和操作 1. 渲染导航 Tab 和高亮 2. 评论列表排序 // 导航 Tab 数组 const tabs = [ { type: 'hot', text: '最热' }, { type: 'time', text: '最新' }, ] const App = () => { // 使用useState管理评论列表,默认值为按照点赞数降序的defaultList(使用lodash库的orderBy方法) const [commentList, setCommentList] = useState(_.orderBy(defaultList, ['like'], ['desc'])) // 评论删除功能 const handleDel = (id) => { console.log(id) // 对commentList进行过滤,过滤掉id等于传入id的评论,留下id不等于传入id的评论 setCommentList(commentList.filter(item => item.rpid !== id)) } // tab切换功能 // 1. 点击谁就把谁的唯一标识type记录下来 // 2. 通过记录的type和每一项遍历时的type做匹配 控制激活类名的显示 const [type, setType] = useState('hot') const handleTabChange = (type) => { console.log(type) setType(type) // 基于列表的排序 if (type === 'hot') { // 最热 => 喜欢点赞数量降序 // lodash函数库,desc降序排序 setCommentList(_.orderBy(commentList, ['like'], ['desc'])) } else { // 最新 => 创建时间降序 // lodash函数库,desc降序排序 setCommentList(_.orderBy(commentList, ['ctime'], ['desc'])) } } 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 */} {tabs.map(item => <span key={item.type} className={`nav-item ${type === item.type ? 'active' : ''}`} onClick = {() => handleTabChange(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={ljAvatar} alt="用户头像" /> </div> </div> <div className="reply-box-wrap"> {/* 评论框 */} <textarea className="reply-box-textarea" placeholder="发一条友善的评论" /> {/* 发布按钮 */} <div className="reply-box-send"> <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> {/* 显示条件: user.id === item.user.id */} {user.uid === item.user.uid && <span className="delete-btn" onClick={() => handleDel(item.rpid)}> 删除 </span> } </div> </div> </div> </div> ) )} <div className="reply-item"> {/* 头像 */} <div className="root-reply-avatar"> <div className="bili-avatar"> <img className="bili-avatar-img" alt="" /> </div> </div> </div> </div> </div> </div> ) } export default App //App.scss .app { width: 80%; margin: 50px auto; } .reply-navigation { margin-bottom: 22px; .nav-bar { display: flex; align-items: center; margin: 0; padding: 0; list-style: none; .nav-title { display: flex; align-items: center; width: 114px; font-size: 20px; .nav-title-text { color: #18191c; font-weight: 500; } .total-reply { margin: 0 36px 0 6px; color: #9499a0; font-weight: normal; font-size: 13px; } } .nav-sort { display: flex; align-items: center; color: #9499a0; font-size: 13px; .nav-item { cursor: pointer; &:hover { color: #00aeec; } &:last-child::after { display: none; } &::after { content: ' '; display: inline-block; height: 10px; width: 1px; margin: -1px 12px; background-color: #9499a0; } } .nav-item.active { color: #18191c; } } } } .reply-wrap { position: relative; } .box-normal { display: flex; transition: 0.2s; .reply-box-avatar { display: flex; align-items: center; justify-content: center; width: 80px; height: 50px; } .reply-box-wrap { display: flex; position: relative; flex: 1; .reply-box-textarea { width: 100%; height: 50px; padding: 5px 10px; box-sizing: border-box; color: #181931; font-family: inherit; line-height: 38px; background-color: #f1f2f3; border: 1px solid #f1f2f3; border-radius: 6px; outline: none; resize: none; transition: 0.2s; &::placeholder { color: #9499a0; font-size: 12px; } &:focus { height: 60px; background-color: #fff; border-color: #c9ccd0; } } } .reply-box-send { position: relative; display: flex; flex-basis: 86px; align-items: center; justify-content: center; margin-left: 10px; border-radius: 4px; cursor: pointer; transition: 0.2s; & .send-text { position: absolute; z-index: 1; color: #fff; font-size: 16px; } &::after { position: absolute; width: 100%; height: 100%; background-color: #00aeec; border-radius: 4px; opacity: 0.5; content: ''; } &:hover::after { opacity: 1; } } } .bili-avatar { position: relative; display: block; width: 48px; height: 48px; margin: 0; padding: 0; border-radius: 50%; } .bili-avatar-img { position: absolute; top: 50%; left: 50%; display: block; width: 48px; height: 48px; object-fit: cover; border: none; border-radius: 50%; image-rendering: -webkit-optimize-contrast; transform: translate(-50%, -50%); } // 评论列表 .reply-list { margin-top: 14px; } .reply-item { padding: 22px 0 0 80px; .root-reply-avatar { position: absolute; left: 0; display: flex; justify-content: center; width: 80px; cursor: pointer; } .content-wrap { position: relative; flex: 1; &::after { content: ' '; display: block; height: 1px; width: 100%; margin-top: 14px; background-color: #e3e5e7; } .user-info { display: flex; align-items: center; margin-bottom: 4px; .user-name { height: 30px; margin-right: 5px; color: #61666d; font-size: 13px; line-height: 30px; cursor: pointer; } } .root-reply { position: relative; padding: 2px 0; color: #181931; font-size: 15px; line-height: 24px; .reply-info { position: relative; display: flex; align-items: center; margin-top: 2px; color: #9499a0; font-size: 13px; .reply-time { width: 86px; margin-right: 20px; } .reply-like { display: flex; align-items: center; margin-right: 19px; .like-icon { width: 14px; height: 14px; margin-right: 5px; color: #9499a0; background-position: -153px -25px; &:hover { background-position: -218px -25px; } } .like-icon.liked { background-position: -154px -89px; } } .reply-dislike { display: flex; align-items: center; margin-right: 19px; .dislike-icon { width: 16px; height: 16px; background-position: -153px -153px; &:hover { background-position: -217px -153px; } } .dislike-icon.disliked { background-position: -154px -217px; } } .delete-btn { cursor: pointer; &:hover { color: #00aeec; } } } } } } .reply-none { height: 64px; margin-bottom: 80px; color: #99a2aa; font-size: 13px; line-height: 64px; text-align: center; } // index.js import { createRoot } from 'react-dom/client' import App from './App' const root = createRoot(document.querySelector('#root')) root.render(<App />)

标签:

React:B站评论demo,实现列表渲染、删除按钮显示和功能实现、导航栏渲染切换及高亮显示、评论区的排序由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“React:B站评论demo,实现列表渲染、删除按钮显示和功能实现、导航栏渲染切换及高亮显示、评论区的排序