🚀一个完美的React入门项目《TodoList》进阶篇---CRUD

1,508 阅读9分钟

前言

继上次的 "💥一个完美的react入门项目《TodoList---待办事项》 - 掘金 (juejin.cn)" 完结后,答应朋友们的带有 "增删改查" 功能(CRUD)功能的的进阶版的《TodoList》来了,在巩固上一次学习的的基础上,只增加一个新的知识点 ”自定义Hooks“。

如果你是一个前端新手,或者说React新手的话,那这个精巧的小项目,将非常适合你,作为一个完美的入门项目,它将保持一个简洁但高级的样式,这样我们就可以专注于创建组件、使用和传递状态、处理事件、属性使用等概念!

如果想和更多的小伙伴交流讨论也可以在联系我,我们有一个专门的完全免费的交流群🥰!!!

✅项目展示

9496c05c085bda2126b29585856a7dd4.gif

🚀初始化项目

在【文件浏览器】中创建项目的根目 ---> 使用VSCode编辑器打开根目录,接着在编辑器中打开【终端】,并保持路径和根目录一致 ---> 在终端输入命令 npm create vite ./ 回车,按照提示选择配置即可 ---> 选择完毕输入命令 npm i 安装依赖性 ---> 安装完毕输入命令 npm run dev 启动项目。 如图所示:

a22b31b2b7414584c5b8abcddf34e9c3.png

😋小白提示:

  • 这里我选择的配置是:框架 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),因为自己淋过雨,所以想给你们撑把伞的未来前端技术专家,最后希望大家都能通过这个小案例有所收获!!!