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,以及如何实现删除笔记操作的文章就介绍到这了,更多相关React localStorage删除笔记内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
