【实战】十一、看板页面及任务组页面开发(三) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十五)

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
    • 十、用 react-query 获取数据,管理缓存
    • 十一、看板页面及任务组页面开发
      • 1~3
      • 4~6
      • 7.编辑任务功能
      • 8.看板和任务删除功能


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom^18.2.0
react-router & react-router-dom^6.11.2
antd^4.24.8
@commitlint/cli & @commitlint/config-conventional^17.4.4
eslint-config-prettier^8.6.0
husky^8.0.3
lint-staged^13.1.2
prettier2.8.4
json-server0.17.2
craco-less^2.0.0
@craco/craco^7.1.0
qs^6.11.0
dayjs^1.11.7
react-helmet^6.1.0
@types/react-helmet^6.1.6
react-query^6.1.0
@welldone-software/why-did-you-render^7.0.1
@emotion/react & @emotion/styled^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 一、项目起航:项目初始化与配置

二、React 与 Hook 应用:实现项目列表

  • 二、React 与 Hook 应用:实现项目列表

三、TS 应用:JS神助攻 - 强类型

  • 三、 TS 应用:JS神助攻 - 强类型

四、JWT、用户认证与异步请求

  • 四、 JWT、用户认证与异步请求(上)

  • 四、 JWT、用户认证与异步请求(下)

五、CSS 其实很简单 - 用 CSS-in-JS 添加样式

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上)

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下)

六、用户体验优化 - 加载中和错误状态处理

  • 六、用户体验优化 - 加载中和错误状态处理(上)

  • 六、用户体验优化 - 加载中和错误状态处理(中)

  • 六、用户体验优化 - 加载中和错误状态处理(下)

七、Hook,路由,与 URL 状态管理

  • 七、Hook,路由,与 URL 状态管理(上)

  • 七、Hook,路由,与 URL 状态管理(中)

  • 七、Hook,路由,与 URL 状态管理(下)

八、用户选择器与项目编辑功能

  • 八、用户选择器与项目编辑功能(上)

  • 八、用户选择器与项目编辑功能(下)

九、深入React 状态管理与Redux机制

  • 九、深入React 状态管理与Redux机制(一)

  • 九、深入React 状态管理与Redux机制(二)

  • 九、深入React 状态管理与Redux机制(三)

  • 九、深入React 状态管理与Redux机制(四)

  • 九、深入React 状态管理与Redux机制(五)

十、用 react-query 获取数据,管理缓存

  • 十、用 react-query 获取数据,管理缓存(上)

  • 十、用 react-query 获取数据,管理缓存(下)

十一、看板页面及任务组页面开发

1~3

  • 十一、看板页面及任务组页面开发(一)

4~6

  • 十一、看板页面及任务组页面开发(二)

7.编辑任务功能

接下来新建编辑任务的组件:

先准备好调用编辑任务接口和获取任务详情的 Hook,编辑 src\utils\task.ts

...
import { useAddConfig, useEditConfig } from "./use-optimistic-options";export const useEditTask = (queryKey: QueryKey) => {const client = useHttp();return useMutation((params: Partial<Task>) =>client(`tasks/${params.id}`, {method: "PATCH",data: params,}),useEditConfig(queryKey));
};export const useTask = (id?: number) => {const client = useHttp();return useQuery<Task>(["task", id], () => client(`tasks/${id}`), {enabled: Boolean(id),});
};

编辑 src\screens\ViewBoard\utils.ts(新增 useTasksModal):

...
// import { useDebounce } from "utils";
import { useTask } from "utils/task";
...export const useTasksSearchParams = () => {const [param] = useUrlQueryParam(["name","typeId","processorId","tagId",]);const projectId = useProjectIdInUrl();// const debouncedName = useDebounce(param.name)return useMemo(() => ({projectId,typeId: Number(param.typeId) || undefined,processorId: Number(param.processorId) || undefined,tagId: Number(param.tagId) || undefined,// name: debouncedName,name: param.name,}),// [projectId, param, debouncedName][projectId, param]);
};...export const useTasksModal = () => {const [{ editingTaskId }, setEditingTaskId] = useUrlQueryParam(['editingTaskId'])const { data: editingTask, isLoading } = useTask(Number(editingTaskId))const startEdit = useCallback((id: number) => {setEditingTaskId({editingTaskId: id})}, [setEditingTaskId])const close = useCallback(() => {setEditingTaskId({editingTaskId: ''})}, [setEditingTaskId])return {editingTaskId,editingTask,startEdit,close,isLoading}
}

视频中使用 useDebounce 使得完全停止输入后才开始搜索,避免输入过程中频繁搜索造成系统资源浪费,且影响用户体验,博主这样更改后中文输入法无法正常使用。。。后续再解决

新建组件:src\screens\ViewBoard\components\taskModal.tsx

import { useForm } from "antd/lib/form/Form"
import { useTasksModal, useTasksQueryKey } from "../utils"
import { useEditTask } from "utils/task"
import { useEffect } from "react"
import { Form, Input, Modal } from "antd"
import { UserSelect } from "components/user-select"
import { TaskTypeSelect } from "components/task-type-select"const layout = {labelCol: {span: 8},wrapperCol: {span: 16}
}export const TaskModal = () => {const [form] = useForm()const { editingTaskId, editingTask, close } = useTasksModal()const { mutateAsync: editTask, isLoading: editLoading } = useEditTask(useTasksQueryKey())const onCancel = () => {close()form.resetFields()}const onOk = async () => {await editTask({...editingTask, ...form.getFieldsValue()})close()}useEffect(() => {form.setFieldsValue(editingTask)}, [form, editingTask])return <ModalforceRender={true}onCancel={onCancel}onOk={onOk}okText={"确认"}cancelText={"取消"}confirmLoading={editLoading}title={"编辑任务"}open={!!editingTaskId}><Form {...layout} initialValues={editingTask} form={form}><Form.Itemlabel={"任务名"}name={"name"}rules={[{ required: true, message: "请输入任务名" }]}><Input /></Form.Item><Form.Item label={"经办人"} name={"processorId"}><UserSelect defaultOptionName={"经办人"} /></Form.Item><Form.Item label={"类型"} name={"typeId"}><TaskTypeSelect /></Form.Item></Form></Modal>
}

注意:与 Drawer 一样,在Modal 组件中使用通过 useForm() 提取的 form 绑定的 Form 时,需要添加 forceRender 属性,否则在页面打开时绑定不到会有报错,参见:【实战】React 实战项目常见报错 —— Instance created by ‘useForm’ is not connected to any Form element. Forget…

编辑:src\screens\ViewBoard\index.tsx(引入 TaskModal):

...
import { TaskModal } from "./components/taskModal";export const ViewBoard = () => {...return (<ViewContainer>...<TaskModal/></ViewContainer>);
};
...

编辑:src\screens\ViewBoard\components\ViewboardCloumn.tsx(引入 useTasksModal 使得点击 任务卡片 可以打开 TaskModal 进行编辑):

...
import { useTasksModal, useTasksSearchParams } from "../utils";
...export const ViewboardColumn = ({ viewboard }: { viewboard: Viewboard }) => {...const { startEdit } = useTasksModal()return (<Container>...<TasksContainer>{tasks?.map((task) => (<Card onClick={() => startEdit(task.id)} style={{ marginBottom: "0.5rem", cursor: 'pointer' }} key={task.id}>...</Card>))}...</TasksContainer></Container>);
};
...

查看功能和效果,点击 任务卡片 后 TaskModal 出现,编辑并确认后即可看到修改后的任务(用了乐观更新,完全无感):
在这里插入图片描述

8.看板和任务删除功能

接下来先实现一个小功能,搜索结果中关键字高亮

新建 src\screens\ViewBoard\components\mark.tsx

export const Mark = ({name, keyword}: {name: string, keyword: string}) => {if(!keyword) {return <>{name}</>}const arr = name.split(keyword)return <>{arr.map((str, index) => <span key={index}>{str}{index === arr.length -1 ? null : <span style={{ color: '#257AFD' }}>{keyword}</span>}</span>)}</>
}

编辑 src\screens\ViewBoard\components\ViewboardCloumn.tsx(引入 Task 并将 TaskCard 单独提取出来):

...
import { Task } from "types/Task";
import { Mark } from "./mark";...const TaskCard = ({task}: {task: Task}) => {const { startEdit } = useTasksModal();const { name: keyword } = useTasksSearchParams()return <CardonClick={() => startEdit(task.id)}style={{ marginBottom: "0.5rem", cursor: "pointer" }}key={task.id}><p><Mark keyword={keyword} name={task.name}/></p><TaskTypeIcon id={task.id} /></Card>
}export const ViewboardColumn = ({ viewboard }: { viewboard: Viewboard }) => {const { data: allTasks } = useTasks(useTasksSearchParams());const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);return (<Container><h3>{viewboard.name}</h3><TasksContainer>{tasks?.map((task) => <TaskCard task={task}/>)}<CreateTask kanbanId={viewboard.id} /></TasksContainer></Container>);
};
...

查看效果:

在这里插入图片描述

下面开始开发删除功能

编辑 src\utils\viewboard.ts(创建并导出 useDeleteViewBoard):

...
export const useDeleteViewBoard = (queryKey: QueryKey) => {const client = useHttp();return useMutation((id?: number) =>client(`kanbans/${id}`, {method: "DELETE",}),useDeleteConfig(queryKey));
};

编辑 src\screens\ViewBoard\components\ViewboardCloumn.tsx

...
import { Button, Card, Dropdown, MenuProps, Modal, Row } from "antd";
import { useDeleteViewBoard } from "utils/viewboard";...export const ViewboardColumn = ({ viewboard }: { viewboard: Viewboard }) => {const { data: allTasks } = useTasks(useTasksSearchParams());const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);return (<Container><Row><h3>{viewboard.name}</h3><More viewboard={viewboard}/></Row><TasksContainer>{tasks?.map((task) => <TaskCard task={task}/>)}<CreateTask kanbanId={viewboard.id} /></TasksContainer></Container>);
};const More = ({ viewboard }: { viewboard: Viewboard }) => {const {mutateAsync: deleteViewBoard} = useDeleteViewBoard(useViewBoardQueryKey())const startDelete = () => {Modal.confirm({okText: '确定',cancelText: '取消',title: '确定删除看板吗?',onOk() {deleteViewBoard(viewboard.id)}})}const items: MenuProps["items"] = [{key: 1,label: "删除",onClick: startDelete,},];return <Dropdown menu={{ items }}><Button type="link" onClick={(e) => e.preventDefault()}>...</Button></Dropdown>
}
...

测试一下删除看板,功能正常

下面是删除任务功能

编辑 src\utils\task.ts(创建并导出 useDeleteTask):

...
export const useDeleteTask = (queryKey: QueryKey) => {const client = useHttp();return useMutation((id?: number) =>client(`tasks/${id}`, {method: "DELETE",}),useDeleteConfig(queryKey));
};

编辑 src\screens\ViewBoard\components\taskModal.tsx

...
import { useDeleteTask, useEditTask } from "utils/task";export const TaskModal = () => {...const { mutateAsync: deleteTask } = useDeleteTask(useTasksQueryKey());...const startDelete = () => {close();Modal.confirm({okText: '确定',cancelText: '取消',title: '确定删除看板吗?',onOk() {deleteTask(Number(editingTaskId));}})}return (<Modal {...}><Form {...}>...</Form><div style={{ textAlign: 'right' }}><Button style={{fontSize: '14px'}} size="small" onClick={startDelete}>删除</Button></div></Modal>);
};

测试一下删除任务,功能正常


部分引用笔记还在草稿阶段,敬请期待。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/47554.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

antd5源码调试环境启动(MacOS)

将源码下载至本地 这里antd5 版本是5.8.3 $ git clone gitgithub.com:ant-design/ant-design.git $ cd ant-design $ npm install $ npm start前提&#xff1a;安装python3、node版本18.14.0(这是本人当前下载的版本&#xff09; python3安装教程可参考&#xff1a;https://…

Stable Diffusion入门修炼手册

简介 作为新入门的新手&#xff0c;通常安装完Stable Diffusion之后&#xff0c;一打开界面&#xff0c;在文生图输入girl或者dog&#xff0c;结果出来的画面比较糟糕&#xff0c;看起来像素很低&#xff0c;画面不清晰&#xff0c;人物也不怎么美&#xff0c;等等其他问题&am…

电脑运行缓慢?4个方法,加速电脑运行!

“我电脑才用了没多久哎&#xff01;怎么突然就变得运行很缓慢了呢&#xff1f;有什么方法可以加速电脑运行速度吗&#xff1f;真的很需要&#xff0c;看看我吧&#xff01;” 电脑的运行速度快会让用户在使用电脑时感觉愉悦&#xff0c;而电脑运行缓慢可能会影响我们的工作效率…

【vue】更改角色权限后,实现页面不刷新更改其可展示的导航菜单

登入的角色本身属于领导级别&#xff08;集团权限&#xff09;&#xff0c;没有下级的不同权限&#xff1a; 切换不同身份&#xff08;公司&#xff09;&#xff0c;以获得相应部门的不同导航菜单及权限 这里实现&#xff1a;更改角色权限后&#xff0c;实现页面 不刷新 更改…

攻防世界-supersqli

原题 解题思路 直接查找看不到明显的回显变化 先找回显变化数量 -1 order by 2 #如果是3列就报错&#xff0c;说明只有两列。接下来找数据库名称&#xff1a; -1 union select 1,databases # 结果是后端做了一些简单的过滤&#xff0c;需要更换查找语句。 -1; show …

LeetCode669. 修剪二叉搜索树

669. 修剪二叉搜索树 文章目录 [669. 修剪二叉搜索树](https://leetcode.cn/problems/trim-a-binary-search-tree/)一、题目二、题解方法一&#xff1a;递归法方法二&#xff1a;迭代法 一、题目 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 hig…

七夕节日表白:七大网页风格与其适用人群

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

如何使用 ChatGPT 将文本转换为 PowerPoint 演示文稿

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 步骤 1&#xff1a;将文本转换为幻灯片演示文稿 第一步涉及指示 ChatGPT 根据给定的文本生成具有特定数量幻灯片的演示文稿。首先&#xff0c;您必须向 ChatGPT 提供要转换的文本。 使用以下提示指示…

SpringMVC-2-Spring MVC拦截器详解:从入门到精通

SpringMVC-2-Spring MVC拦截器详解&#xff1a;从入门到精通 今日目标 能够编写拦截器并配置拦截器 1.拦截器【理解】 1 拦截器介绍 1.1 拦截器概念和作用 拦截器&#xff08;Interceptor&#xff09;是一种动态拦截方法调用的机制&#xff0c;在SpringMVC中动态拦截控制器方…

C的进阶C++学习方向

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的…

Python开发环境(Visual Studio Code、Anaconda、PyInstaller、Enigma Virtual Box)

Python开发环境 [Anaconda、PyInstaller、Enigma Virtual Box] AnacondaAnaconda安装搭建Python环境Anaconda命令 Visual Studio CodeVisual Studio Code中Python设置Visual Studio Code中安装PyQt5Visual Studio Code中使用Qt DesignerVisual Studio Code中Anaconda切换虚拟环…

仓库管理的重点在哪?仓库管理能有哪些软件?

对于做实体生意的中小商户来说&#xff0c;仓库管理工作是重中之重的&#xff0c;仓库管理的好坏&#xff0c;直接影响着门店销售和财务状况。 但对于很多中小商户来说&#xff0c;没有足够的人力和精力去高效地做好仓库管理工作&#xff0c;而借助仓库管理软件或进销存软件来…

SNAT和DNAT

SNAT和DNAT 一、SNAT策略及应用1.1SNAT策略概述1.2开启SNAT的命令1.2.1 临时打开1.2.2永久打开 1.3SNAT转换1&#xff1a;固定的公网IP地址1.4SNAT转换2&#xff1a;非固定的公网IP地址&#xff08;共享动态IP地址&#xff09;1.5SNAT案例1.5.1实验准备1.5.2配置网关服务器&…

IDEA启动报错【java.sql.SQLSyntaxErrorException: ORA-00904: “P“.“PRJ_NO“: 标识符无效】

IDEA报错如下&#xff1a; 2023-08-17 11:26:15.535 ERROR [egrant-biz,b48324d82fe23753,b48324d82fe23753,true] 24108 --- [ XNIO-1 task-1] c.i.c.l.c.RestExceptionController : 服务器异常org.springframework.jdbc.BadSqlGrammarException: ### Error queryin…

PDF怎么转成PPT文件免费?一个软件解决

随着科技的不断发展和进步&#xff0c;电子文档已经成为我们日常工作和学习中不可或缺的一部分。PDF作为一种跨平台的文件格式&#xff0c;以其可靠性和易读性而备受推崇。然而&#xff0c;在某些情况下&#xff0c;我们可能需要PDF怎么转成PPT文件免费&#xff0c;以便更好地展…

六大需求管理工具:满足您的需求管理需求

在项目开发和产品设计中&#xff0c;需求管理是确保项目成功的关键所在。通过使用专业的需求管理工具&#xff0c;团队可以更好地追踪、记录和分析需求&#xff0c;提高工作效率并确保项目按时完成。 市场上有许多需求管理工具可供选择&#xff0c;当下有什么好用的需求管理工…

FlashAttention算法详解

这篇文章的目的是详细的解释Flash Attention&#xff0c;为什么要解释FlashAttention呢&#xff1f;因为FlashAttention 是一种重新排序注意力计算的算法&#xff0c;它无需任何近似即可加速注意力计算并减少内存占用。所以作为目前LLM的模型加速它是一个非常好的解决方案&…

拒绝摆烂!C语言练习打卡第五天

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;每日一练 &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、选择题 &#x1f4dd;1.第一题 &#x1f4dd;2.第二题 &#x1f4d…

Linux的基础指令

目录 1、ls指令 .和..意义 2、pwd指令 3、cd指令 ①cd ~ ②cd - 关于cd ..的用法 绝对路径和相对路径 4、touch指令 5、mkdir指令 tree指令 6、rmdir指令 7、rm指令 * 8、man指令 9、cp指令 nano&#xff1a; 10、mv指令 11、cat指令 12、more指令 13、less…

0009Java程序设计-jsp在线学习平台设计与实现

摘 要目 录系统实现开发环境 摘 要 在线学习平台&#xff0c;是一个利用因特网作为平台传送教学内容&#xff0c;实施网上教学&#xff0c;进行网上交流和学习的信息系统。构建在线学习系统平台&#xff0c;可以克服传统课堂教育的局限性&#xff0c;形成一种主动的、协作的、…