React自学:如何使用localStorage,以及如何实现删除笔记操作
1. 初始化notes
以下这段代码完成了这些操作:
- 调用
localStorage.getItem("notes")从浏览器的本地存储中获取名为 “notes” 的数据。 - 使用
JSON.parse将获取到的字符串解析成数组。 - 如果本地存储中没有 “notes” 数据(返回值为 null),则默认将 notes 设置为空数组 []。
const [notes, setNotes] = React.useState(JSON.parse(localStorage.getItem("notes")) || [])
useState钩子
useState 是 React 的一个钩子,用于在函数组件中引入状态。
它返回一个数组,有两个元素:
- 当前状态值(这里是 notes)。
- 更新状态的函数(这里是 setNotes)。
localStorage
localStorage是浏览器提供的 API,用于在本地存储键值对数据。localStorage.getItem("notes")从localStorage中获取键为 “notes” 的数据,返回的结果是一个字符串。
JSON.parse
localStorage 中存储的所有数据都是字符串。
JSON.parse 将字符串解析为 JavaScript 对象。
- 如果存储的数据是一个 JSON 字符串,例如:“[1, 2, 3]”,调用
JSON.parse后会得到 [1, 2, 3]。
|| 运算符
- || 是逻辑或运算符,用来提供一个默认值。
- 如果
localStorage.getItem("notes")返回 null(即没有找到 “notes” 键),JSON.parse(localStorage.getItem("notes"))的结果会是 null。 - 在这种情况下,表达式的右侧([])会被返回,表示 notes 的初始值是一个空数组。
2. 每次notes发生改变时,将notes保存到localStorage
React.useEffect(() => {localStorage.setItem("notes", JSON.stringify(notes))}, [notes])
useEffect 是 React 的一个钩子,用于在函数组件中处理副作用。
- 副作用通常指与组件渲染逻辑无关的行为,例如:数据获取、订阅、手动 DOM 操作、或者日志记录等。
它的语法如下:
React.useEffect(effectFunction, dependencies);
effectFunction 是一个函数,在特定条件下运行。
dependencies 是一个数组,控制 effectFunction 的运行时机。
localStorage.setItem 是浏览器提供的 API,用于向 localStorage 中存储键值对。
它接受两个参数:
- 键:存储数据的名称(这里是 “notes”)。
- 值:存储的具体数据,必须是字符串。
JSON.stringify(notes):
- 将 notes 转换为 JSON 格式的字符串,因为
localStorage只能存储字符串数据。
当组件渲染后并且 notes 发生变化时:
- useEffect 会被触发。
- localStorage.setItem(“notes”, JSON.stringify(notes)) 将最新的 notes 数组保存到本地存储中。
如果 notes 没有变化:
- 即使组件重新渲染,useEffect 不会运行,因为 notes 的值没有改变。
3. 什么是 Lazy State Initialization?
通常情况下,useState 的初始值是直接计算出来的:
const [state, setState] = React.useState(computeInitialState());
- 这里
computeInitialState()会在组件每次渲染时立即执行,即使结果只需要在初次渲染时使用。 - 如果
computeInitialState是一个复杂的计算函数,就会浪费性能。
为了解决这个问题,React 提供了一种惰性初始化的方法:通过向 useState 传递一个函数,而不是直接传递计算结果。这种函数只会在组件第一次渲染时执行,之后不会再次调用。
惰性初始化
const [state, setState] = React.useState(() => computeInitialState());
- 当传递一个函数给 useState 时,React 只会在组件初次渲染时调用这个函数来计算初始状态。
- 后续的状态更新不再调用此函数。
4. 在React中实现删除笔记的操作
<button className="delete-btn"onClick={(event) => props.deleteNote(event, note.id)}
><i className="gg-trash trash-icon"></i>
</button>
<button> 元素
- HTML 的按钮标签,用于定义一个可点击的交互元素。
- 在 React 中, 可以绑定事件和自定义属性,并触发相关的事件处理程序。
回调函数中的 (event) => props.deleteNote(event, note.id) 是一个箭头函数,执行时调用 props.deleteNote 方法,并将两个参数传递给它:
event:原生的点击事件对象,提供有关点击的信息(如目标元素、鼠标位置等)。note.id:当前笔记的唯一标识符,用于指定要删除的具体笔记。
<i> 是 HTML 的行内元素,通常用作图标的占位符。
function deleteNote(event, noteId){event.stopPropagation()setNotes(oldNotes => oldNotes.filter(note => note.id !== noteId))}
event.stopPropagation()
作用:
- 阻止事件从当前元素传播到父元素或其他祖先元素(即阻止事件冒泡)。
- 防止删除按钮的点击事件触发父组件的其他事件处理逻辑(如整个笔记项的点击事件)。
场景举例:
假设笔记项的外层组件有一个点击事件绑定:
<div onClick={() => console.log("Note clicked!")}><button onClick={(event) => deleteNote(event, noteId)}>Delete</button>
</div>
如果没有 event.stopPropagation():
- 点击删除按钮时,既会触发
deleteNote,又会触发外层div的onClick。
有了 event.stopPropagation():
- 点击删除按钮时,只会触发
deleteNote。
箭头函数 oldNotes => oldNotes.filter(...)
setNotes 接收一个更新函数,该函数的参数是当前的状态值 oldNotes。
filter 方法:
- 返回一个新数组,其中包含满足条件的所有元素。
- 条件:保留 id 不等于 noteId 的笔记,即删除 noteId 对应的笔记。
完整逻辑
通过 filter 遍历 oldNotes 数组:
- 如果 note.id !== noteId,该笔记被保留。
- 如果 note.id === noteId,该笔记被过滤掉。
返回的新数组赋值给 notes,并触发组件重新渲染。
5. 删除按钮的CSS实现
.delete-btn {display: none;background: none;border: none;
}
作用
- 定义删除按钮的初始样式,默认情况下按钮是隐藏的。
属性解释
-
display: none;:
隐藏元素,按钮不占据布局空间,不可见。 -
background: none;:
移除按钮的默认背景样式。 -
border: none;:
移除按钮的默认边框。
.title:hover > .delete-btn {display: block;
}
作用
- 当用户将鼠标悬停在 .title 元素上时,其子元素 .delete-btn 显示出来。
属性解释
display: block;:
- 让 .delete-btn 可见,并以块级元素形式显示。
> .delete-btn:
- 表示只选择直接子元素 .delete-btn,避免影响其他嵌套更深的 .delete-btn。
实现逻辑
- 通过伪类
:hover,动态切换按钮的显示状态,提供更好的用户交互体验。
.trash-icon {cursor: pointer;
}
作用
- 定义垃圾桶图标的样式,使其在用户鼠标悬停时具有点击效果。
属性解释
cursor: pointer;:
- 鼠标悬停时显示手型指针,表示该元素可点击。
.gg-trash {box-sizing: border-box;position: relative;display: block;transform: scale(var(--ggs,1));width: 10px;height: 12px;border: 2px solid transparent;box-shadow:0 0 0 2px,inset -2px 0 0,inset 2px 0 0;border-bottom-left-radius: 1px;border-bottom-right-radius: 1px;margin-top: 4px;
}
作用
- 定义垃圾桶图标的外观,包括大小、形状和整体样式。
属性解释
box-sizing: border-box;:
- 控制元素的宽高计算方式,包含内边距和边框。
position: relative;:
- 定义元素为相对定位,用于配合子元素的绝对定位。
transform: scale(var(--ggs,1));:
- 使用 CSS 变量 --ggs 控制缩放比例,默认为 1。
width: 10px; height: 12px;:
- 定义垃圾桶的宽度和高度。
border: 2px solid transparent;:
- 设置透明的边框。
box-shadow:
为垃圾桶形状添加外边框和内部边框:
- 0 0 0 2px:外部边框,2px 宽。
- inset -2px 0 0 和 inset 2px 0 0:内部分隔线。
border-bottom-left-radius 和 border-bottom-right-radius:
- 为垃圾桶底部的两个角添加圆角。
margin-top: 4px;:
- 在顶部增加间距。
.gg-trash::after {background: currentColor;border-radius: 3px;width: 16px;height: 2px;top: -4px;left: -5px;
}
作用
- 添加垃圾桶的横梁部分(通常表示垃圾桶的盖子)。
属性解释
background: currentColor;:
- 使用当前文本颜色作为背景颜色。
border-radius: 3px;:
- 添加圆角,使盖子边缘更平滑。
width: 16px; height: 2px;:
- 定义横梁的大小。
top: -4px; left: -5px;:
- 使用绝对定位将横梁放置在垃圾桶顶部的位置。
.gg-trash::before {width: 10px;height: 4px;border: 2px solid;border-bottom: transparent;border-top-left-radius: 2px;border-top-right-radius: 2px;top: -7px;left: -2px;
}
作用
- 添加垃圾桶的盖子部分(弯曲的顶部结构)。
属性解释
width: 10px; height: 4px;:
- 定义盖子的宽度和高度。
border: 2px solid;:
- 设置盖子的边框。
border-bottom: transparent;:
- 移除盖子底部的边框,使其开口朝下。
border-top-left-radius 和 border-top-right-radius:
- 设置盖子顶部的两个角为圆角。
top: -7px; left: -2px;:
- 使用绝对定位将盖子放置在垃圾桶顶部。
总结:垃圾桶图标的整体实现
- .gg-trash 是垃圾桶的主体,包括边框、阴影等基础结构。
- ::after 添加横梁(垃圾桶盖的下部分)。
- ::before 添加盖子顶部的弯曲结构。
结合这些样式,实现了一个完整的垃圾桶图标。
交互效果总结
- .delete-btn 默认隐藏,用户鼠标悬停在 .title 上时显示。
- 鼠标悬停时,垃圾桶图标变为可点击状态,通过样式 cursor: pointer 提供视觉提示。
6. 查找当前笔记id
const [currentNoteId, setCurrentNoteId] = React.useState((notes[0]?.id) || "")
const currentNote = notes.find(note => note.id === currentNoteId) || notes[0]
React.useState:
- 定义一个状态变量
currentNoteId和其对应的更新函数setCurrentNoteId。
notes[0]?.id:
- 通过可选链操作符 (?.),尝试访问数组
notes中第一项的 id。 - 如果 notes 数组为空或者
notes[0]为undefined,notes[0]?.id返回undefined而不会报错。
|| "":
如果 notes[0]?.id 为 undefined,currentNoteId 的初始值设置为空字符串 ""。
效果
- 如果 notes 数组非空,
currentNoteId的初始值是第一项笔记的 id。 - 如果 notes 数组为空,
currentNoteId的初始值是 “”。
notes.find(note => note.id === currentNoteId):
- 使用
Array.prototype.find()方法在 notes 数组中查找 id 等于currentNoteId的笔记。 find方法返回第一个满足条件的元素。如果没有找到匹配的元素,返回undefined。
|| notes[0]:
如果没有找到匹配的笔记(即 find 返回 undefined),使用 || 提供默认值,返回 notes[0](数组的第一项)。
相关文章:
React自学:如何使用localStorage,以及如何实现删除笔记操作
1. 初始化notes 以下这段代码完成了这些操作: 调用 localStorage.getItem("notes") 从浏览器的本地存储中获取名为 “notes” 的数据。使用 JSON.parse 将获取到的字符串解析成数组。如果本地存储中没有 “notes” 数据(返回值为 null&#…...
go语言使用websocket发送一条消息A,持续接收返回的消息
在Go语言中实现一个WebSocket客户端,可以使用gorilla/websocket这个非常流行的库来处理WebSocket连接。下面是一个简单的示例,展示了如何创建一个WebSocket客户端,向服务器发送消息"A",并持续接收来自服务器的响应。 首…...
如何对小型固定翼无人机进行最优的路径跟随控制?
控制架构 文章继续采用的是 ULTRA-Extra无人机,相关参数如下: 这里用于guidance law的无人机运动学模型为: { x ˙ p V a cos γ cos χ V w cos γ w cos χ w y ˙ p V a cos γ sin χ V w cos γ w sin χ…...
C++常见面试题-初级2
1. C和C有什么区别? C是面向对象的语言,而C是面向过程的语言;C引入new/delete运算符,取代了C中的malloc/free库函数;C引入引用的概念,而C中没有;C引入类的概念,而C中没有࿱…...
Spring Security 6 系列之二 - 基于数据库的用户认证和认证原理
之所以想写这一系列,是因为之前工作过程中使用Spring Security,但当时基于spring-boot 2.3.x,其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0,结果一看Spring Security也升级为6.3.0,关键是其风…...
mfc140.dll是什么东西?mfc140.dll缺失的几种具体解决方法
mfc140.dll是Microsoft Foundation Classes(MFC)库中的一个动态链接库(DLL)文件,它是微软基础类库的一部分,为Windows应用程序的开发提供了丰富的类库和接口。MFC库旨在简化Windows应用程序的开发过程&…...
【STM32 Modbus编程】-作为主设备写入多个线圈和寄存器
作为主设备写入多个线圈和寄存器 文章目录 作为主设备写入多个线圈和寄存器1、硬件准备与连接1.1 RS485模块介绍1.2 硬件配置与接线1.3 软件准备2、写入多个线圈2.1 数据格式2.2 发送数据2.3 结果3、写入多个寄存器3.1 数据格式3.2 发送数据3.3 结果本文将实现STM32作为ModBus主…...
Windows安全中心(病毒和威胁防护)的注册
文章目录 Windows安全中心(病毒和威胁防护)的注册1. 简介2. WSC注册初探3. WSC注册原理分析4. 关于AMPPL5. 参考 Windows安全中心(病毒和威胁防护)的注册 本文我们来分析一下Windows安全中心(Windows Security Center…...
微积分复习笔记 Calculus Volume 2 - 4.2 Direction Fields and Numerical Methods
4.2 Direction Fields and Numerical Methods - Calculus Volume 2 | OpenStax...
深入理解旋转位置编码(RoPE)及其在大型语言模型中的应用
文章目录 前言一、 旋转位置编码原理1、RoPE概述2、 复数域内的旋转1、位置编码生成2、 应用位置编码二、RoPE的实现细节1、RotaryEmbedding类设计2、apply_rotary_pos_emb函数3、demo_apply_rotary_pos_emb函数三、完整RoPE代码Demo前言 随着自然语言处理(NLP)领域的快速发…...
内网穿透的应用-在OpenWrt上轻松搭建SFTP服务,安全传输文件不再难!
文章目录 前言1. 安装openssh-sftp-server2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT系统上安装SFTP服务,并结合cpolar内网穿透,创建安全隧道映射22端口,实现在公网环境下远程OpenWRT SFTP&#…...
【图像处理lec3、4】空间域的图像增强
目录 1. 空间域图像增强的背景与目标 2. 空间域处理的数学描述 3. 灰度级变换 4. 幂律变换(Power-Law Transformation) 5、 分段线性变换 Case 1: 对比度拉伸 Case 2: 灰度切片 Case 3: 按位切片 6、对数变换(Logarithmic Transform…...
【算法day13】二叉树:递归与回溯
题目引用 找树左下角的值路径总和从中序与后序遍历构造二叉树 今天就简简单单三道题吧~ 1. 找到树左下角的值 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 我们…...
上海亚商投顾:创业板指缩量下跌 多只高位股午后跌停
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 市场全天震荡调整,创业板指领跌,高位股开始出现退潮,建设工业、星光股份、…...
单步调试Android Framework——App冷启动
纸上得来终觉浅,绝知此事要躬行。 —— [宋]陆游 基于aosp_cf_x86_64_phone-trunk_staging-eng , 下面是具体断点位置。 第一部分,桌面launcher进程 com.android.launcher3.touch.ItemClickHandler onClickonClickAppShortcutstartAppShor…...
统计一个目录下的文件及目录数量-linux010
要统计一个目录下的文件数量(包括子目录中的文件),可以使用以下命令: 1. 统计所有文件数量(包括子目录) 在终端中运行以下命令: find /path/to/directory -type f | wc -l 解释:…...
spring RestTemplate使用说明
rest-template是spring对httpclient的逻辑封装,它底层还是基于httpclient,所以一些配置其实跟httpclient是强相关的。 基本配置 rest-template可以不带参数,使用默认配置,也可以指定ClientHttpRequestFactory参数,Cl…...
thinkphp:try-catch捕获异常
使用简单的例子,实现了一个简单的try-catch捕获异常的实例 //开始事务Db::startTrans(); try{ //有异常抛出异常 if(存在错误){ throw new \Exception("异常信息"); } // 提交事务 Db::commit(); // 返回成功信息 ... } catch (\…...
shardingsphere分库分表跨库访问 添加分片规则
shardingsphere分库分表跨库访问 添加分片规则 建立 JDBC 环境 创建表 t_order: CREATE TABLE t_order (tid bigint(20) NOT NULL,tname varchar(255) DEFAULT NULL,goods_id bigint(20) DEFAULT NULL,tstatus varchar(255) DEFAULT NULL,PRIMARY KEY (tid) ) E…...
c++:std::map下标运算符的不合理使用
这是我分析之前遗留代码时发现的一个隐藏点;不过我并不认为这样使用std::map是合理的。 看看简化后的代码,v1、v2的值应该是多少呢? #include <map>std::map<int, int> cm[2];int get_cm_value(int device, int ctrl) { auto …...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
