【react案例】实现评论列表

1. 需求

  1. 展示评论列表
  2. 实现删除功能
    2.1 只有自己的评论才展示删除按钮
    2.2 点击删除按钮,删除当前评论
  3. tab切换(点击对应tab,对tab文案高亮处理)
  4. 评论高亮
  5. 评论排序(最新、最热)

2. 实现思路

  1. useState维护评论列表
  2. map方法遍历渲染
  3. 删除显示------条件渲染
  4. 删除功能------拿到当前评论项的id,然后对原有评论列表进行过滤,根据id剔除要删除的评论项
  5. tab高亮------记录点击的tab的type,根据type去匹配高亮样式
  6. 评论排序------根据tab的type对评论列表状态数据进行不同的排序处理,当成新值传给set方法重新渲染视图UI(注意:排序后不要修改评论列表状态数据的原始值,教程里推荐使用lodash包的orderBy方法)

3. 代码

  • 样式是我自己随便写的,具体可以参考b站本身的css样式
3.1 App.js
javascript 复制代码
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
javascript 复制代码
.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教程

相关推荐
一袋米扛几楼9810 分钟前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴23 分钟前
前端与后端的区别与联系
前端
EnCi Zheng1 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen1 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技1 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人1 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实1 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha1 小时前
三目运算符
linux·服务器·前端
晓晨的博客1 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect1 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript