React自学:如何使用localStorage,以及如何实现删除笔记操作

1. 初始化notes

以下这段代码完成了这些操作:

  1. 调用 localStorage.getItem("notes") 从浏览器的本地存储中获取名为 "notes" 的数据。
  2. 使用 JSON.parse 将获取到的字符串解析成数组。
  3. 如果本地存储中没有 "notes" 数据(返回值为 null),则默认将 notes 设置为空数组 []。
javascript 复制代码
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

javascript 复制代码
  React.useEffect(() => {
    localStorage.setItem("notes", JSON.stringify(notes))
  }, [notes])

useEffect 是 React 的一个钩子,用于在函数组件中处理副作用。

  • 副作用通常指与组件渲染逻辑无关的行为,例如:数据获取、订阅、手动 DOM 操作、或者日志记录等。

它的语法如下:

javascript 复制代码
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 的初始值是直接计算出来的:

javascript 复制代码
const [state, setState] = React.useState(computeInitialState());
  • 这里 computeInitialState() 会在组件每次渲染时立即执行,即使结果只需要在初次渲染时使用。
  • 如果 computeInitialState 是一个复杂的计算函数,就会浪费性能。

为了解决这个问题,React 提供了一种惰性初始化的方法:通过向 useState 传递一个函数,而不是直接传递计算结果。这种函数只会在组件第一次渲染时执行,之后不会再次调用。

惰性初始化

javascript 复制代码
const [state, setState] = React.useState(() => computeInitialState());
  • 当传递一个函数给 useState 时,React 只会在组件初次渲染时调用这个函数来计算初始状态。
  • 后续的状态更新不再调用此函数。

4. 在React中实现删除笔记的操作

javascript 复制代码
<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 方法,并将两个参数传递给它:

  1. event:原生的点击事件对象,提供有关点击的信息(如目标元素、鼠标位置等)。
  2. note.id:当前笔记的唯一标识符,用于指定要删除的具体笔记。

<i> 是 HTML 的行内元素,通常用作图标的占位符。

javascript 复制代码
  function deleteNote(event, noteId){
    event.stopPropagation()
    setNotes(oldNotes => oldNotes.filter(note => note.id !== noteId))
  }

event.stopPropagation()

作用:

  • 阻止事件从当前元素传播到父元素或其他祖先元素(即阻止事件冒泡)。
  • 防止删除按钮的点击事件触发父组件的其他事件处理逻辑(如整个笔记项的点击事件)。

场景举例:

假设笔记项的外层组件有一个点击事件绑定:

javascript 复制代码
<div onClick={() => console.log("Note clicked!")}>
    <button onClick={(event) => deleteNote(event, noteId)}>Delete</button>
</div>

如果没有 event.stopPropagation()

  • 点击删除按钮时,既会触发 deleteNote,又会触发外层 divonClick

有了 event.stopPropagation()

  • 点击删除按钮时,只会触发 deleteNote

箭头函数 oldNotes => oldNotes.filter(...)
setNotes 接收一个更新函数,该函数的参数是当前的状态值 oldNotes

filter 方法:

  • 返回一个新数组,其中包含满足条件的所有元素。
  • 条件:保留 id 不等于 noteId 的笔记,即删除 noteId 对应的笔记。

完整逻辑

通过 filter 遍历 oldNotes 数组:

  • 如果 note.id !== noteId,该笔记被保留。
  • 如果 note.id === noteId,该笔记被过滤掉。

返回的新数组赋值给 notes,并触发组件重新渲染。

5. 删除按钮的CSS实现

css 复制代码
.delete-btn {
  display: none;
  background: none;
  border: none;
}

作用

  • 定义删除按钮的初始样式,默认情况下按钮是隐藏的。

属性解释

  • display: none;:

    隐藏元素,按钮不占据布局空间,不可见。

  • background: none;:

    移除按钮的默认背景样式。

  • border: none;:

    移除按钮的默认边框。

css 复制代码
.title:hover > .delete-btn {
  display: block;
}

作用

  • 当用户将鼠标悬停在 .title 元素上时,其子元素 .delete-btn 显示出来。

属性解释
display: block;

  • 让 .delete-btn 可见,并以块级元素形式显示。

> .delete-btn

  • 表示只选择直接子元素 .delete-btn,避免影响其他嵌套更深的 .delete-btn。

实现逻辑

  • 通过伪类 :hover,动态切换按钮的显示状态,提供更好的用户交互体验。
css 复制代码
.trash-icon {
  cursor: pointer;
}

作用

  • 定义垃圾桶图标的样式,使其在用户鼠标悬停时具有点击效果。

属性解释
cursor: pointer;

  • 鼠标悬停时显示手型指针,表示该元素可点击。
css 复制代码
.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-radiusborder-bottom-right-radius

  • 为垃圾桶底部的两个角添加圆角。

margin-top: 4px;

  • 在顶部增加间距。
css 复制代码
.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;

  • 使用绝对定位将横梁放置在垃圾桶顶部的位置。
css 复制代码
.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 提供视觉提示。
相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
wdfk_prog9 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
ouliten9 小时前
cuda编程笔记(36)-- 应用Tensor Core加速矩阵乘法
笔记·cuda
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端