前言
继上次的 "💥一个完美的react入门项目《TodoList---待办事项》 - 掘金 (juejin.cn)" 完结后,答应朋友们的带有 "增删改查" 功能(CRUD)功能的的进阶版的《TodoList》来了,在巩固上一次学习的的基础上,只增加一个新的知识点 ”自定义Hooks“。
如果你是一个前端新手,或者说React新手的话,那这个精巧的小项目,将非常适合你,作为一个完美的入门项目,它将保持一个简洁但高级的样式,这样我们就可以专注于创建组件、使用和传递状态、处理事件、属性使用等概念!
如果想和更多的小伙伴交流讨论也可以在联系我,我们有一个专门的完全免费的交流群🥰!!!
✅项目展示
🚀初始化项目
在【文件浏览器】中创建项目的根目 ---> 使用VSCode编辑器打开根目录,接着在编辑器中打开【终端】,并保持路径和根目录一致 ---> 在终端输入命令 npm create vite ./
回车,按照提示选择配置即可 ---> 选择完毕输入命令 npm i
安装依赖性 ---> 安装完毕输入命令 npm run dev
启动项目。 如图所示:
😋小白提示:
-
这里我选择的配置是:框架 React 、语言 JS、
🌈构建React组件
1️⃣事项表单 (ItemForm)
// ItemForm.jsx
import { useState } from 'react'
import { PlusIcon } from '@heroicons/react/24/solid'
const ItemForm = ({ addTask }) => {
const [task, setTask] = useState('')
const handleFormSubmit = (e) => {
e.preventDefault()
addTask({
name: task,
checked: false,
id: Date.now()
})
setTask('')
}
return (
<form
className='todo'
onSubmit={handleFormSubmit}
>
<div className="wrapper">
<input
type="text"
id='task'
className='input'
value={task}
onInput={(e) => setTask(e.target.value)}
required
autoFocus
maxLength={120}
placeholder='请输入'
/>
<label
htmlFor="task"
className='label'
>请输入...</label>
</div>
<button
className='btn'
aria-label='添加待办事项'
type='submit'
>
<PlusIcon />
</button>
</form>
)
}
export default ItemForm
😋小白提示:
-
这里使用到了
@heroicons
库,先在项目根目录下安装该图标库,安装命令npm i @heroicons
-
这是一个 React 函数组件,名为 ItemForm,它接受一个名为 addTask 的 props。
-
在函数组件中,它使用了 React 自带的 Hooks,调用 useState 方法创建了一个状态变量 task 和一个方法 setTask,用于管理输入框中的文本值。
-
handleFormSubmit 方法则是用于处理表单提交事件的回调函数,当表单被提交时,它会使用 addTask 方法将一个新的待办事项对象添加到任务列表中,并将文本输入框的值重置为一个空字符串。
-
组件的返回值是一个包含表单元素和一个添加按钮的 JSX 元素。表单元素包含一个文本输入框和一个标签元素,标签元素的文本内容为 "请输入...",它的 htmlFor 属性和文本输入框的 id 属性相同,以便实现标签和文本输入框的关联。
-
添加按钮使用了 @heroicons/react 库中的 PlusIcon 进行渲染,它被包含在一个按钮元素中,按钮元素的类型为 submit,表示它会触发表单的提交事件。在按钮元素上还提供了一个 aria-label 属性,用于为屏幕阅读器提供可访问性支持。
-
总体来说,这个组件实现了一个待办事项输入框和添加按钮,它负责处理用户的输入,并将新的待办事项添加到任务列表中。
2️⃣事项列表(TaskList)
// TaskList.jsx
// 样式
import styles from './TaskList.module.css'
//组件
import TaskItem from './TaskItem'
const TaskList = ({ tasks, deleteTask, toggleTask, enterEditMode }) => {
return (
<ul className={styles.tasks}>
{
tasks.sort((a, b) => b.id - a.id).map(task => (
<TaskItem
key={task.id}
task={task}
deleteTask={deleteTask}
toggleTask={toggleTask}
enterEditMode={enterEditMode}
/>
))
}
</ul>
)
}
export default TaskList
😋小白提示:
这是一个 React 函数组件,名为 TaskList,它接受了一个名为 tasks 的 props,这个 props 是一个包含待办事项的数组。
组件的返回值是一个包含多个 TaskItem 组件的无序列表元素,每个 TaskItem 组件都对应着一个待办事项。tasks 数组首先被排序,以确保最新的待办事项显示在列表的顶部。
在每个 TaskItem 组件的渲染过程中,它们被传递了以下 props:
- key:每个组件必须拥有一个唯一的 key 属性,以便 React 在更新组件时能够正确地识别它们。
- task:待办事项对象,包含了 id、name 和 checked 这三个属性,用于显示待办事项的名称和状态。
- deleteTask:用于删除待办事项的回调函数。
- toggleTask:用于切换待办事项完成状态的回调函数。
- enterEditMode:用于进入编辑模式的回调函数。
TaskList 组件还使用了一个 CSS 模块化的方式来管理样式,样式代码被定义在一个名为 TaskList.module.css 的文件中,并被导入到了组件文件中。样式类名为 tasks,它被应用到了组件的根元素上,以便为组件提供样式支持。
总体来说,这个组件实现了一个待办事项列表,它将任务列表中的每一项都传递给 TaskItem 组件进行渲染,并提供删除、切换状态和进入编辑模式的回调函数,以便在用户进行相应操作时更新任务列表。
3️⃣待办事项(TaskItem)
// TaskItem.jsx
import React, { useState } from 'react'
import { CheckIcon, PencilSquareIcon, TrashIcon } from '@heroicons/react/24/outline';
// 样式
import styles from './TaskItem.module.css'
const TaskItem = ({ task, deleteTask, toggleTask, enterEditMode }) => {
const [isChecked, setIsChecked] = useState(task.checked)
const handleCheckboxChange = () => {
setIsChecked(!isChecked)
toggleTask(task.id)
}
return (
<li className={styles.task}>
<div className={styles["task-group"]}>
<input
type="checkbox"
className={styles.checkbox}
checked={isChecked}
onChange={handleCheckboxChange}
name={task.name}
id={task.id}
/>
<label htmlFor={task.id} className={styles.label}>
{task.name}
<p className={styles.checkmark}>
<CheckIcon strokeWidth={2} width={24} height={24} />
</p>
</label>
</div>
<div className={styles["task-group"]}>
<button
className='btn'
aria-label={`更新 ${task.name} 事项`}
onClick={() => enterEditMode(task)}
>
<PencilSquareIcon width={24} height={24} />
</button>
<button
className={`btn ${styles.delete}`}
aria-label={`删除 ${task.name} 事项`}
onClick={() => deleteTask(task.id)}
>
<TrashIcon width={24} height={24} />
</button>
</div>
</li>
)
}
export default TaskItem
😋小白提示:
- 这是一个 React 函数组件,名为 TaskItem,它接受了一个名为 task 的 props,这个 props 是一个待办事项对象。
- 组件的返回值是一个包含两个按钮和一个复选框的列表元素,它根据传递进来的任务对象渲染了相应的内容。
- 在复选框的状态被改变时,组件会使用 useState 方法创建一个 isChecked 状态变量,并在 handleCheckboxChange 方法中更新 isChecked 的值。handleCheckboxChange 方法还调用了 toggleTask 方法,向父组件发送一个切换任务完成状态的请求。
- TaskItem 组件还包含了两个按钮,一个是编辑按钮,另一个是删除按钮。这两个按钮都使用了 @heroicons/react 库中的图标进行渲染。编辑按钮被点击时,它会调用 enterEditMode 方法,以便将任务切换到编辑模式。删除按钮被点击时,它会调用 deleteTask 方法,以便删除任务。
- TaskItem 组件还使用了 CSS 模块化的方式来管理样式,样式代码被定义在一个名为 TaskItem.module.css 的文件中,并被导入到了组件文件中。样式类名为 task、task-group、checkbox、label 和 checkmark,它们被应用到了组件的不同元素中,以便为组件提供样式支持。
- 总体来说,这个组件实现了一个待办事项的列表项,它包含一个复选框、任务名称、编辑按钮和删除按钮,它还提供了复选框状态变化、编辑和删除等多个回调函数,以便在用户进行相应操作时更新任务列表。
4️⃣ 编辑表单(EditForm)
// EditForm.jsx
import { useState, useEffect } from "react"
import { CheckIcon } from "@heroicons/react/24/solid"
const EditFrom = ({ editedTask, updateTask }) => {
const [updatedTaskName, setUpdatedTaskName] = useState('')
const handleFormSubmit = (e) => {
e.preventDefault()
updateTask({ ...editedTask, name: updatedTaskName })
}
return (
<div
role="dialog"
aria-labelledby="editTask"
// onClick={ }
>
<form
className="todo"
onSubmit={handleFormSubmit}
>
<div className="wrapper">
<input
type="text"
id="editTask"
className='input'
value={updatedTaskName}
onInput={e => setUpdatedTaskName(e.target.value)}
required
autoFocus
maxLength={60}
placeholder="更新事项"
/>
<label htmlFor="editTask" className="label">更新事项</label>
</div>
<button
className="btn"
aria-label="确定更新编辑事项"
type="submit"
>
<CheckIcon width={24} height={24} strokeWidth={2} />
</button>
</form>
</div>
)
}
export default EditFrom
😋小白提示:
-
这是一个 React 函数组件,名为 EditFrom,它接受了两个 props:editedTask 和 updateTask,分别代表当前正在编辑的任务和更新任务的回调函数。
-
组件的返回值是一个包含一个表单和一个提交按钮的 div 元素,它包含一个文本输入框和一个 label 元素,用于让用户输入任务名称,并提供一个提交按钮用于向父组件发送更新任务请求。
-
在表单提交时,组件会使用 useState 方法创建一个 updatedTaskName 状态变量,并在 handleFormSubmit 方法中更新它的值。handleFormSubmit 方法还调用了 updateTask 方法,向父组件发送一个更新任务的请求,同时将当前正在编辑的任务的名称更新为 updatedTaskName 的值。
-
EditFrom 组件还使用了 useEffect 方法,在组件挂载后将 editedTask 的名称设置为 updatedTaskName 的初始值。
-
组件还使用了 @heroicons/react 库中的 CheckIcon 图标进行渲染,以及 CSS 模块化的方式来管理样式。
-
总体来说,这个组件实现了一个用于编辑任务名称的表单,它包含一个文本输入框和一个提交按钮,提供了更新任务的回调函数,并使用 useEffect 方法将当前任务名称设置为输入框的初始值,以便让用户能够方便地进行编辑。
💫自定义Hooks函数
相较上一篇,这是新增的知识点,这也是本篇最精彩的部分🤗!!!
import { useEffect, useState } from 'react'
const useLocalStorage = (key, initialValue) => {
const [value, setValue] = useState(() => {
try {
const localValue = window.localStorage.getItem(key)
return localValue ? JSON.parse(localValue) : initialValue
} catch (err) {
console.log(err)
return initialValue
}
})
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(value))
}, [key, value])
return [value, setValue]
}
export default useLocalStorage
😋小白提示:
- 这是一个自定义的 React Hooks 函数,名为 useLocalStorage,用于将某个值存储在本地存储中。它接受两个参数:key 和 initialValue,分别代表要存储的数据的键名和初始值。
- 函数内部使用了 useState 和 useEffect 这两个 React Hooks,useState 方法用于创建一个名为 value 的状态变量和一个名为 setValue 的更新函数,它们管理着存储在本地存储中的值。
- 在 useState 方法中,它使用了一个函数作为初始值,这个函数会首先尝试从本地存储中获取 key 对应的值,如果获取成功则将其转换为 JSON 对象,否则返回 initialValue。
- 在 useEffect 方法中,它监听了 key 和 value 的变化,当它们发生变化时,会将最新的 value 值以 JSON 字符串的形式存储到本地存储中。
- 最后,它返回一个数组,包含两个元素:value 和 setValue,它们分别代表当前存储在本地存储中的值和更新该值的函数。
- 总体来说,这个自定义 Hooks 函数实现了将某个值存储在本地存储中,并提供了获取和更新该值的方法,以便在应用中进行状态管理。
最后
以上就是 “🚀一个完美的React入门项目《TodoList》进阶篇---CRUD ” 的全部内容,如果理解起来还是有困难的,可以看一看上一篇 💥一个完美的react入门项目《TodoList---待办事项》 - 掘金 (juejin.cn) 的内容,也希望大家可以给我 ”点赞、关注、评论“ ,这也是我持续下去的动力,我是多动症男孩(ddboy),因为自己淋过雨,所以想给你们撑把伞的未来前端技术专家,最后希望大家都能通过这个小案例有所收获!!!