当前位置: 首页 > news >正文

吉林省建设信息网站中国教育培训网

吉林省建设信息网站,中国教育培训网,网站建设主持词,合肥互联网公司文章目录 一、项目起航:项目初始化与配置二、React 与 Hook 应用:实现项目列表三、TS 应用:JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

文章目录

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


学习内容来源: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&8

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

9.拖拽实现

接下来的内容将会是整个课程最难的部分,相关知识也不是很常用

  • 安装拖拽功能库 react-beautiful-dnd 及相关类型文件库
npm i react-beautiful-dnd ## --force
npm i @types/react-beautiful-dnd -D ## --force 

接下来在原功能库的基础上自行二次封装

新建 src\components\grag-and-drop.tsx

import React, { ReactNode } from "react"
import { Draggable, DraggableProps, Droppable, DroppableProps, DroppableProvided, DroppableProvidedProps } from "react-beautiful-dnd"// 删除原有 “函数类型” children,使用 ReactNode 类型的 children
type DropProps = Omit<DroppableProps, 'children'> & { children: ReactNode }export const Drop = ({ children, ...props }: DropProps) => {return (<Droppable {...props}>{(provided) => {if (React.isValidElement(children)) {return React.cloneElement(children, {...provided.droppableProps,ref: provided.innerRef,provided,});}return <div />;}}</Droppable>);
};type DropChildProps =Partial<{ provided: DroppableProvided } & DroppableProvidedProps> &React.HTMLAttributes<HTMLDivElement>export const DropChild = React.forwardRef<HTMLDivElement, DropChildProps>(({children, ...props}, ref) => <div ref={ref} {...props} >{children}{props.provided?.placeholder}</div>
);type DragProps = Omit<DraggableProps, 'children'> & { children: ReactNode }
export const Drag = ({children, ...props}: DragProps) => {return <Draggable {...props}>{(provided => {if(React.isValidElement(children)) {return React.cloneElement(children, {...provided.draggableProps,...provided.dragHandleProps,ref: provided.innerRef})}return <div/>})}</Draggable>
}

forwardRef 是用作转发的,经过包裹后的组件可以传入 ref 属性

  • Refs 转发 – React

这步报错:ref: provided.innerRef

Argument of type '{ ref: (element: HTMLElement | null) => void; 'data-rbd-drag-handle-draggable-id'?: string | undefined; 'data-rbd-drag-handle-context-id'?: string | undefined; 'aria-describedby'?: string | undefined; ... 7 more ...; onTransitionEnd?: React.TransitionEventHandler<...> | undefined; }' is not assignable to parameter of type 'Partial<unknown> & Attributes'.Object literal may only specify known properties, and 'ref' does not exist in type 'Partial<unknown> & Attributes'.

接下来使用这个组件

编辑 src\screens\ViewBoard\index.tsx

...
import { DragDropContext } from "react-beautiful-dnd";
import { Drag, Drop, DropChild } from "components/grag-and-drop";export const ViewBoard = () => {useDocumentTitle("看板列表");const { data: currentProject } = useProjectInUrl();const { data: viewboards, isLoading: viewBoardIsLoading } = useViewboards(useViewBoardSearchParams());const { isLoading: taskIsLoading } = useTasks(useTasksSearchParams());const isLoading = taskIsLoading || viewBoardIsLoading;return (<DragDropContext onDragEnd={() => {}}><ViewContainer><h1>{currentProject?.name}看板</h1><SearchPanel />{isLoading ? (<Spin />) : (<Drop type='COLUMN' direction='horizontal' droppableId="viewboard"><ColumnsContainer>{viewboards?.map((vbd, index) => (<Drag key={vbd.id} draggableId={'viewboard' + vbd.id} index={index}><ViewboardColumn viewboard={vbd} key={vbd.id} /></Drag>))}<CreateViewBoard /></ColumnsContainer></Drop>)}<TaskModal /></ViewContainer></DragDropContext>);
};export const ColumnsContainer = styled(DropChild)`display: flex;overflow-x: scroll;flex: 1;
`;

编辑 src\screens\ViewBoard\components\ViewboardCloumn.tsx 使组件可以透传 props 以及通过 forwardRef 转发 传入 ref:

...
export const ViewboardColumn = React.forwardRef<HTMLDivElement, { viewboard: Viewboard }>(({ viewboard, ...props }, ref) => {const { data: allTasks } = useTasks(useTasksSearchParams());const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);return (<Container {...props} ref={ref}><Row><h3>{viewboard.name}</h3><More viewboard={viewboard} key={viewboard.id}/></Row><TasksContainer>{tasks?.map((task) => (<TaskCard key={task.id} task={task} />))}<CreateTask kanbanId={viewboard.id} /></TasksContainer></Container>);
});

10.拖拽持久化

拖拽的时候 看板之间的间隔应该是不变的

编辑 src\screens\ViewBoard\index.tsx(调整组件层级并显式使用 DropChild):

...
export const ViewBoard = () => {...return (<DragDropContext onDragEnd={() => {}}><ViewContainer><h1>{currentProject?.name}看板</h1><SearchPanel />{isLoading ? (<Spin />) : (<ColumnsContainer><Drop type="COLUMN" direction="horizontal" droppableId="viewboard"><DropChild style={{display: 'flex'}}>{viewboards?.map((vbd, index) => (<Dragkey={vbd.id}draggableId={"viewboard" + vbd.id}index={index}><ViewboardColumn viewboard={vbd} key={vbd.id} /></Drag>))}</DropChild></Drop><CreateViewBoard /></ColumnsContainer>)}<TaskModal /></ViewContainer></DragDropContext>);
};export const ColumnsContainer = styled.div`display: flex;overflow-x: scroll;flex: 1;
`;

接下来做 任务拖拽排序

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

...
import { Drag, Drop, DropChild } from "components/grag-and-drop";
...
export const ViewboardColumn = React.forwardRef<HTMLDivElement,{ viewboard: Viewboard }
>(({ viewboard, ...props }, ref) => {const { data: allTasks } = useTasks(useTasksSearchParams());const tasks = allTasks?.filter((task) => task.kanbanId === viewboard.id);return (<Container {...props} ref={ref}><Row><h3>{viewboard.name}</h3><More viewboard={viewboard} key={viewboard.id} /></Row><TasksContainer><Drop type="Row" direction="vertical" droppableId={'task' + viewboard.id}><DropChild>{tasks?.map((task, taskIndex) => (<Dragkey={task.id}draggableId={"task" + task.id}index={taskIndex}><TaskCard key={task.id} task={task} /></Drag>))}</DropChild></Drop><CreateTask kanbanId={viewboard.id} /></TasksContainer></Container>);
});

拖拽功能好了,接下来将拖拽结果持久化到数据库中

编辑 src\utils\use-optimistic-options.ts(看板和任务排序 获取URL参数,为后续乐观更新做准备):

...
export const useReorderViewboardConfig = (queryKey: QueryKey) =>useConfig(queryKey, (target, old) => old ? [old, ...target] : []);export const useReorderTaskConfig = (queryKey: QueryKey) =>useConfig(queryKey, (target, old) => old || []);

编辑 src\utils\viewboard.ts(新增看板排序接口的 Custom Hook, SortPropsuseReorderTask 共用):

...
export interface SortProps {// 要重新排序的 itemfromId: number;// 目标 itemreferenceId: number;// 放在目标 Item 的前还是后type: 'before' | 'after';fromKanbanId?: number;toKanbanId?: number;
}export const useReorderViewboard = () => {const client = useHttp();return useMutation((params: SortProps) => {return client("kanbans/reorder", {data: params,method: "POST",});}, useReorderViewboardConfig(queryKey));
};

编辑 src\utils\task.ts(新增看板排序接口的 Custom Hook):

...
export const useReorderTask = (queryKey: QueryKey) => {const client = useHttp();return useMutation((params: SortProps) => {return client("tasks/reorder", {data: params,method: "POST",});}, useReorderTaskConfig(queryKey));
};

编辑 src\screens\ViewBoard\index.tsx(完善之前预留的 onDragEnd):

...
import { useReorderViewboard, useViewboards } from "utils/viewboard";
import {useProjectInUrl,useTasksQueryKey,useTasksSearchParams,useViewBoardQueryKey,useViewBoardSearchParams,
} from "./utils";
...
import { useReorderTask, useTasks } from "utils/task";
...
import { useCallback } from "react";export const ViewBoard = () => {...const onDragEnd = useDragEnd();return (<DragDropContext onDragEnd={onDragEnd}>...</DragDropContext>);
};export const useDragEnd = () => {const { data: viewboards } = useViewboards(useViewBoardSearchParams());const { mutate: reorderViewBoard } = useReorderViewboard(useViewBoardQueryKey());const { mutate: reorderTask } = useReorderTask(useTasksQueryKey());const { data: allTasks = [] } = useTasks(useTasksSearchParams());return useCallback(({ source, destination, type }: DropResult) => {if (!destination) {return;}// 看板排序if (type === "COLUMN") {const fromId = viewboards?.[source.index].id;const toId = viewboards?.[destination.index].id;if (!fromId || !toId || fromId === toId) {return;}const type = destination.index > source.index ? "after" : "before";reorderViewBoard({ fromId, referenceId: toId, type });}if (type === "ROW") {const fromKanbanId = +source.droppableId;const toKanbanId = +destination.droppableId;if (fromKanbanId === toKanbanId) {return;}const fromTask = allTasks.filter((task) => task.kanbanId === fromKanbanId)[source.index];const toTask = allTasks.filter((task) => task.kanbanId === toKanbanId)[destination.index];if (fromTask?.id === toTask?.id) {return;}reorderTask({fromId: fromTask?.id,referenceId: toTask?.id,fromKanbanId,toKanbanId,type:fromKanbanId === toKanbanId && destination.index > source.index? "after": "before",});}},[viewboards, reorderViewBoard, allTasks, reorderTask]);
};
...

至此,拖拽持久化完成,查看效果验证


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

http://www.ds6.com.cn/news/32381.html

相关文章:

  • 雅布设计创始人杭州余杭区抖音seo质量高
  • 模板形的网站制作百度应用下载
  • 益阳 网站制作维护常见的营销手段
  • 杭州余杭网站制作手机网站模板免费下载
  • 常州网站推广机构怎么自己做网址
  • 最好的网站建设系统聚合搜索引擎入口
  • 网站开发的要求杭州seo推广排名稳定
  • 网站开发建设付款方式外贸网站建设平台
  • 网站建设 风险说明哪个平台可以买卖链接
  • 做营销网站建设码迷seo
  • 网站做弹窗广告吗下载优化大师
  • 有没有专业做特产的网站扶贫832网络销售平台
  • 国内永久免费crm游戏什么是搜索引擎优化推广
  • wordpress评分点评网站关键词排名seo
  • 怎么样自己建设一个网站搜索引擎优化指的是
  • 做网站公司北京北京网站seo
  • 网站建设服务器如何选择优化网站搜索排名
  • vmware做网站步骤seo优化教程自学网
  • 网页制作工具 免费拼多多seo 优化软件
  • 珠海做企业网站多少钱百度统计api
  • 自己做的网站百度搜到专注网络营销推广公司
  • 网站常用模块微博付费推广有用吗
  • 网站做2微码广州代运营公司有哪些
  • 南京每月做社保明细在哪个网站查网络推广外包要多少钱
  • 级a做爰片免费视网站百度霸屏培训
  • 智慧团建登录入口seo推广软件怎样
  • 河南工程建设信息网站百度app下载安装
  • 重庆做网站优化西部数码域名注册
  • 数据网站有哪些佛山网页搜索排名提升
  • 广州 深圳 外贸网站建设企业网站建设方案论文