【React】通过实际示例详解评论列表渲染和删除

文章目录

    • 一、引言
    • 二、初始状态与状态更新
      • 1. 使用`useState`钩子管理状态
      • 2. 评论列表的初始数据
    • 三、列表渲染的实现
      • 1. `list.map(item => { ... })`
      • 2. `return` 语句
      • 3. JSX 语法
      • 4. 为什么这样设计
      • 5. 完整解读
    • 四、列表项的唯一标识
      • 1. `key` 的作用
      • 2. `key` 的用法
      • 3. 可以没有 `key` 吗?
      • 4. 示例代码中的 `key`
    • 五、处理评论删除
      • 1. 删除评论的实现
      • 2. 条件渲染删除按钮
    • 六、完整代码示例

在React开发中,列表渲染是一个非常常见且重要的功能。本文将通过一个实际的评论列表渲染例子,详细介绍如何使用React的useState钩子和数组方法动态渲染和更新列表。本文内容从基础到进阶,涵盖了核心概念和实现方法,帮助开发者掌握React中的列表渲染技术。

一、引言

在现代Web开发中,动态数据的渲染和更新是常见需求。React作为一个强大的前端框架,通过其灵活的状态管理和组件化设计,使得处理这些需求变得更加简单和高效。本文将通过一个完整的App组件代码,详细解析其中的评论列表渲染和更新的实现。

二、初始状态与状态更新

1. 使用useState钩子管理状态

在React函数组件中,useState钩子用于声明状态变量并提供更新函数。以下代码声明了一个初始评论列表的状态变量list,并使用setList函数来更新它:

const [list, setList] = useState(defaultList);

2. 评论列表的初始数据

const defaultList = [{rpid: 3,user: {uid: '13258165',avatar: '',uname: '周杰伦',},content: '哎哟,不错哦',ctime: '10-18 08:15',like: 88,},// 其他评论数据...
];

三、列表渲染的实现

在React中,使用map方法可以方便地将数组数据转换为React元素。以下代码展示了如何遍历list数组并生成评论列表:

<div className="reply-list">{list.map(item => {return (<div key={item.rpid} className="reply-item">{/* 头像 */}<div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"src={item.user.avatar}alt=""/></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.uid === item.user.uid && (<spanclassName="delete-btn"onClick={() => onDelete(item.rpid)}>删除</span>)}</div></div></div></div>)})}
</div>

1. list.map(item => { ... })

1.1. map 函数

map 是 JavaScript 中数组的一个方法,用于遍历数组中的每个元素,并返回一个新的数组。map 方法接收一个回调函数作为参数,回调函数会对数组中的每个元素执行操作,然后返回新数组中的元素。具体语法如下:

const newArray = array.map(callback(element, index, array));
  • callback 是对每个元素执行的函数。
  • element 是当前处理的元素。
  • index 是当前元素的索引。
  • array 是被遍历的数组。

在这段代码中,map 方法用于遍历 list 数组中的每个 item,并返回包含 JSX 元素的数组。

1.2. 箭头函数 (item => { ... })

箭头函数是 ES6 中引入的一种更简洁的函数表达方式。箭头函数没有自己的 this 绑定,而是从作用域链的上一层继承 this。它的语法如下:

const functionName = (parameter) => { ... };

在这段代码中,箭头函数 item => { ... } 用于定义一个回调函数,其中 itemlist 数组中的每个元素。

2. return 语句

在箭头函数的回调函数内部,return 语句用于返回一个 JSX 元素,这个元素将被放入新的数组中。具体来说,return 语句返回了一个包含评论项的 div 元素。

3. JSX 语法

JSX 是 JavaScript 的一种语法扩展,用于描述 UI 结构。它看起来很像 HTML,但实际上是 React.createElement() 的语法糖。React 会将 JSX 代码转换为 JavaScript 对象,从而构建虚拟 DOM。

在这段代码中,JSX 用于描述每个评论项的结构。具体的 JSX 代码如下:

<div key={item.rpid} className="reply-item">{/* 列表项的内容 */}
</div>
  • key={item.rpid}key 属性是 React 中用于唯一标识每个列表项的标识符,有助于优化列表的渲染和更新。
  • className="reply-item"className 是 JSX 中用于指定 CSS 类名的属性,相当于 HTML 中的 class

4. 为什么这样设计

4.1. 使用 map 渲染列表

使用 map 函数渲染列表是一种常见的模式,因为它能够简洁而高效地遍历数组,并生成一组对应的 JSX 元素。每个元素都可以根据需要进行个性化渲染。

4.2. 使用 key 属性

在渲染列表时,React 需要一个唯一的 key 属性来区分不同的元素,从而高效地更新和重新渲染组件。key 属性的值应当是唯一的,且尽量稳定,以避免因重新渲染而导致的性能问题或潜在的 Bug。

4.3. 使用箭头函数

箭头函数使得代码更加简洁,同时避免了传统函数中的 this 绑定问题。在这个例子中,箭头函数用于定义 map 函数的回调,使得代码更易于阅读和维护。

5. 完整解读

{list.map(item => {return (<div key={item.rpid} className="reply-item">{/* 列表项的内容 */}</div>)
})}
  • list.map(item => { ... }):对 list 数组中的每个元素 item 执行回调函数。
  • return:返回一个包含评论项的 JSX 元素。
  • <div key={item.rpid} className="reply-item">...</div>:为每个评论项生成一个 div 元素,并指定唯一的 key 属性和 CSS 类名。

四、列表项的唯一标识

在渲染列表时,确保每个列表项都有一个唯一的key属性是至关重要的。这里使用评论的rpid作为key

<div key={item.rpid} className="reply-item">

1. key 的作用

当 React 渲染列表时,它需要一个唯一的标识符来跟踪每个元素,以便在元素发生变化时可以高效地重新渲染和更新。这主要体现在以下几个方面:

元素的识别

key 帮助 React 识别哪些元素发生了变化、被添加或被移除。例如,当我们有一个列表并对其进行重新排序时,key 使 React 能够知道哪些项是相同的,哪些项是新增的或删除的。

提高渲染性能

有了 key,React 可以根据变化最小化 DOM 操作。例如,如果一个列表项的位置发生了变化,而它的 key 没有变,React 只会移动 DOM 元素,而不是销毁旧元素并创建新元素,这大大提高了性能。

防止潜在的 Bug

没有 keykey 不唯一,可能导致一些潜在的 Bug。例如,输入框中的内容可能会丢失,组件的状态可能会出错等。key 保证了每个元素在其父元素中都是独一无二的,这样 React 就可以正确地维护和更新组件的状态。

2. key 的用法

在列表中使用 key 的典型方式如下:

<div key={item.rpid} className="reply-item">{/* 列表项的内容 */}
</div>

这里的 item.rpid 是一个唯一标识符,对于每个列表项来说都是独一无二的。

3. 可以没有 key 吗?

不能没有 key。如果不提供 key,React 会发出警告,告知 key 是必要的。即使不提供 key 的代码能够工作,它也会导致性能下降和潜在的 Bug。因此,始终建议在列表渲染中为每个元素提供一个唯一的 key

4. 示例代码中的 key

代码中,key 的用法如下:

{list.map(item => {return (<div key={item.rpid} className="reply-item">{/* 列表项的内容 */}</div>)
})}

这个 key={item.rpid} 确保了每个 reply-item 元素都有一个唯一标识符 rpid,这样 React 可以高效地更新和重新渲染评论列表。

五、处理评论删除

1. 删除评论的实现

在React中,要删除列表中的某个元素,可以使用数组的filter方法并调用setList来更新状态。以下是删除评论的实现代码:

const onDelete = rpid => {setList(list.filter(item => item.rpid !== rpid));
};

2. 条件渲染删除按钮

只有当前登录用户的评论才会显示删除按钮。这通过条件渲染来实现:

{user.uid === item.user.uid && (<spanclassName="delete-btn"onClick={() => onDelete(item.rpid)}>删除</span>
)}
  1. 条件渲染
{user.uid === item.user.uid && ( ... )}

这一部分使用了逻辑与运算符(&&)来进行条件渲染。在JavaScript中,&&运算符在第一个操作数为真时返回第二个操作数,否则返回第一个操作数。在React的JSX语法中,这种写法常用于条件渲染。

  • user.uid:表示当前登录用户的ID。
  • item.user.uid:表示评论作者的ID。

这段代码的逻辑是:如果user.uid等于item.user.uid(即当前用户是评论的作者),则渲染<span>标签及其内容,否则不渲染任何内容。

  1. <span> 标签
<spanclassName="delete-btn"onClick={() => onDelete(item.rpid)}
>删除
</span>

如果条件成立,这段代码将渲染一个<span>标签,具体解析如下:

className="delete-btn"

  • className:设置元素的CSS类。这里将<span>标签的CSS类设置为"delete-btn",以便应用相应的样式。

onClick={() => onDelete(item.rpid)}

  • onClick:React中的事件处理属性,用于处理点击事件。
  • {() => onDelete(item.rpid)}:这是一个箭头函数,点击时会调用onDelete函数,并传递当前评论的ID(item.rpid)作为参数。

当用户点击这个<span>标签时,onDelete函数会被调用,传递的rpid参数用于识别需要删除的评论。

箭头函数为什么设计成这样

  • 避免立即执行

如果直接写成 onClick={onDelete(item.rpid)},会导致在渲染时立即执行 onDelete 函数,而不是在点击时执行。这样会立即删除评论,而不是等待用户点击删除按钮。因此,需要使用一个函数包装起来,确保 onDelete(item.rpid) 只有在点击事件发生时才执行。

  • 创建事件处理函数

通过定义一个箭头函数 () => onDelete(item.rpid),我们创建了一个新的函数。当点击事件发生时,这个函数会被调用,然后再调用 onDelete 并传递参数 item.rpid

  1. 结合完整示例
  • 如果当前登录用户(user.uid)与评论的作者(item.user.uid)是同一个人,则渲染一个带有"delete-btn"类的<span>标签,显示“删除”文本。
  • 点击“删除”按钮时,调用onDelete函数,并传递当前评论的ID(item.rpid)以删除该评论。

六、完整代码示例

以下是包含所有上述功能的完整代码示例:

import { useState } from 'react';
import './App.scss';
import avatar from './images/bozai.png';
import orderBy from 'lodash/orderBy';const defaultList = [{rpid: 3,user: {uid: '13258165',avatar: '',uname: '周杰伦',},content: '哎哟,不错哦',ctime: '10-18 08:15',like: 88,},{rpid: 2,user: {uid: '36080105',avatar: '',uname: '许嵩',},content: '我寻你千百度 日出到迟暮',ctime: '11-13 11:29',like: 88,},{rpid: 1,user: {uid: '30009257',avatar,uname: '黑马前端',},content: '学前端就来黑马',ctime: '10-19 09:00',like: 66,},
];const user = {uid: '30009257',avatar,uname: '黑马前端',
};const tabs = [{ type: 'hot', text: '最热' },{ type: 'time', text: '最新' },
];const App = () => {const [activeTab, setActiveTab] = useState('hot');const [list, setList] = useState(defaultList);const onDelete = rpid => {setList(list.filter(item => item.rpid !== rpid));};const onToggle = type => {setActiveTab(type);let newList;if (type === 'time') {newList = orderBy(list, 'ctime', 'desc');} else {newList = orderBy(list, 'like', 'desc');}setList(newList);};return (<div className="app"><div className="reply-navigation"><ul className="nav-bar"><li className="nav-title"><span className="nav-title-text">评论</span><span className="total-reply">{list.length}</span></li><li className="nav-sort">{tabs.map(item => {return (<divkey={item.type}className={item.type === activeTab ? 'nav-item active' : 'nav-item'}onClick={() => onToggle(item.type)}>{item.text}</div>);})}</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={avatar} alt="用户头像" /></div></div><div className="reply-box-wrap"><textareaclassName="reply-box-textarea"placeholder="发一条友善的评论"/><div className="reply-box-send"><div className="send-text">发布</div></div></div></div><div className="reply-list">{list.map(item => {return (<div key={item.rpid} className="reply-item"><div className="root-reply-avatar"><div className="bili-avatar"><imgclassName="bili-avatar-img"src={item.user.avatar}alt=""/></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.uid === item.user.uid && (<spanclassName="delete-btn"onClick={() => onDelete(item.rpid)}>删除</span>)}</div></div></div></div>);})}</div></div></div>);
};export default App;

源代码来源


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Stable Diffusion】(基础篇五)—— 使用SD提升分辨率

使用SD提升分辨率 本系列博客笔记主要参考B站nenly同学的视频教程&#xff0c;传送门&#xff1a;B站第一套系统的AI绘画课&#xff01;零基础学会Stable Diffusion&#xff0c;这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 在前期作画的…

c++ 构造函数与析构函数

本文参考菜鸟教程&#xff0c;仅作笔记用。 构造函数 构造函数&#xff08;Constructor&#xff09;是一种特殊的方法&#xff0c;用于在创建对象时进行初始化操作。构造函数的名称与类的名称是完全相同的&#xff0c;并且不会返回任何类型&#xff0c;也不会返回 void。在面…

Pytorch使用教学4-张量的索引

1 张量的符号索引 张量也是有序序列&#xff0c;我们可以根据每个元素在系统内的顺序位置&#xff0c;来找出特定的元素&#xff0c;也就是索引。 1.1 一维张量的索引 一维张量由零维张量构成 一维张量索引与Python中的索引一样是是从左到右&#xff0c;从0开始的&#xff…

搭建NFS、web、dns服务器

目录 1、搭建一个nfs服务器&#xff0c;客户端可以从该服务器的/share目录上传并下载文件 服务端配置&#xff1a; 客户端测试&#xff1a; 2、搭建一个Web服务器&#xff0c;客户端通过www.haha.com访问该网站时能够看到内容:this is haha 服务端配置&#xff1a; 客户端…

【Web爬虫逆向】“企业预警通”模糊查询公司信息,逆向案例实战

“企业预警通”模糊查询公司信息&#xff0c;逆向案例实战 功能介绍效果演示思路分析1、先找到模糊查询的接口2、分析headers与params中参数并进行构造3、JS逆向&#xff0c;跟栈&#xff0c;找到js中key和dataCategory的生成方法&#xff0c;并完成js补码构造4、成功还原key后…

UART编程框架详解

1. UART介绍 UART&#xff1a;通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;简称串口。 调试&#xff1a;移植u-boot、内核时&#xff0c;主要使用串口查看打印信息 外接各种模块 1.1 硬件知识_UART硬件介绍 UART的全称是Unive…

新160个crackme - 011-wocy.1

运行分析 显示Unregister&#xff0c;点击注册无反应&#xff0c;猜测要先注册 PE分析 C 程序&#xff0c;32位&#xff0c;无壳 静态分析 ida发现关键字符串&#xff0c;进入关键函数 动态调试 设置断点动态调试&#xff0c;CWnd::UpdateData(true) &#xff1a;用于将屏幕上控…

MATLAB基础:数据和变量

今天我们开始学习MATLAB基础知识 1、常用非运算符及其作用 1、“,” 作为程序运行的分隔符&#xff0c;起到分隔语句的作用 2、“;” 同样作为分隔符&#xff0c;与“,”不同的是“;”会在程序运行时隐藏该行语句 如下图&#xff1a; 3、“...” 三个英文句点表示续行符…

W30-python03-pytest+selenium+allure访问百度网站实例

此篇文章为总结性&#xff0c;将pystest、selenium、allure结合起来 功能如下&#xff0c;web自动化&#xff0c;输入baidu网站&#xff0c;搜索“雷军”、打开网页中第一条内容 pytestsel.py如下&#xff1a; import time import re import allure import pytest from tools…

提升ROI:利用高级爬虫技术优化营销策略

如何通过高级爬虫技术高效提升营销ROI&#xff1f; 摘要&#xff1a; 在当今数据驱动的营销环境中&#xff0c;提升投资回报率&#xff08;ROI&#xff09;的关键在于精准洞察市场与用户行为。本文将探讨如何运用高级爬虫技术来优化营销策略&#xff0c;从海量互联网数据中挖掘…

【数据分享】2008-2022年我国省市县三级的逐日NO2数据(excel\shp格式)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2022年的省市县三级的逐日PM2.5数据、2013-2022年的省市县三级的逐日CO数据和2013-2022年的省市县三级的逐日SO2数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次…

jmeter实战(1)- Mac环境安装

一、安装 JDK 这个就不介绍了&#xff0c;本地自行安装 JDK 并且配置好环境变量 二、安装 Jmeter 1. 下载地址 —> 下载链接点击这里 2. 选择合适的版本下载 3. 解压到本地目录 解压后&#xff0c;会得到下面的目录文件&#xff1a; 输入cd bin&#xff0c;进入到bin…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

【Android】Activity与Fragment的数据传递

上一篇文章学到了碎片的创建与生命周期&#xff0c;接下来学习碎片的常用操作&#xff0c;其中会用到上一篇文章的三个碎片&#xff0c;就做一个简单的说明吧&#xff1a;LeftFragment&#xff08;包含一个按钮&#xff09;、RightFragment4&#xff08;以粉色为背景的文本&…

408专业课130|零基础五个月速成攻略

计算机考研&#xff0c;有两个选择&#xff0c;一个是自命题&#xff0c;一个是408。如果你只是考一个普通院校&#xff0c;可以选择考自命题院校&#xff0c;容易上岸&#xff0c;但是如果考985/211/这类院校&#xff0c;最好还是选择408&#xff0c;因为408的考风险能力很强&…

Apollo部署与简易架构梳理

文章目录 apollo 安装apollo的基本架构组件机制component编译与加载 节点通讯数据的传输消息读写的实现消息的写端消息读端 常用术语ComponentChannelTaskNodeReader/WriterService/ClientParameter服务发现CRoutineSchedulerMessageDag文件Launch文件Record文件Mainboard Moni…

在图神经网络(GNN)上进行关系推理的新架构

开发能够学习推理的模型是一个众所周知的具有挑战性的问题&#xff0c;在这个领域中&#xff0c;使用图神经网络&#xff08;GNNs&#xff09;似乎是一个自然的选择。然而&#xff0c;以往关于使用GNNs进行推理的工作表明&#xff0c;当这些模型面对需要比训练时更长推理链的测…

(leetcode学习)236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十一章 添加设备树节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

linux禁用root

linux禁用root 1. 禁止普通用户切换到root1.1 sudo -i和sudo -s的区别1.2 sudo -i和直接登录root账号的区别1.3 禁止sudo -i切换root1.4 禁止su - root切换root 2. 禁止root远程登录2.1 ssh禁止root登录2.2 禁止远程桌面登录 本文主要介绍&#xff1a; 如何禁止普通用户切换到r…