当前位置: 首页 > 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/53337.html

相关文章:

  • 网站建设分析报告南京seo优化
  • 揭阳住房和城乡建设厅网站济南网站制作公司
  • 可以做动画的网站都有哪些seo怎么优化简述
  • 西安微信平台网站建设百度排行榜风云
  • 黄金网站app免费视频大全网站收录申请
  • 传统网站和手机网站的区别怎么样优化关键词排名
  • 协会网站建设制作廊坊首页霸屏排名优化
  • 网站建设市场需求分析二维码引流推广的平台
  • 青岛市做网站优化怎么做网站优化排名
  • 做网站的分辨率是72吗国际新闻网
  • 靖江网站推广搜索量查询百度指数
  • 安徽建设厅证书查询网网站怎么做业务推广技巧
  • 网站分站开发计划书seoul什么意思
  • 游戏分类网站怎么做seo关键词怎么选择
  • 个人博客网站需求分析chrome 谷歌浏览器
  • 有关中国文明网联盟网站建设活动方案百度广告平台
  • 专业做网站流程网络营销就是seo正确吗
  • 淮北市建设安全监督站网站搜索引擎优化宝典
  • 做网站如何与美工配合设计一个公司网站多少钱
  • 做网站怎么写代码万能搜索引擎网站
  • 如何开科技网站搜索引擎优化情况怎么写
  • wordpress常常被用来做什么网站百度指数如何提升
  • 泰安小程序网络公司谷歌seo推广招聘
  • 小网站备案北京网络营销公司
  • magento网站建设品牌建设的五个要素
  • 电子商务网站建设内容买域名
  • 昆明市网络建设武汉整站优化
  • 静态网页开发工具苏州网站关键字优化
  • 广州互联网公司泉州seo培训
  • 孵化基地网站怎么建设windows优化大师是什么