【实战】 六、用户体验优化 - 加载中和错误状态处理(上) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(八)

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
      • 1.给页面添加 Loading 和 Error 状态,增加页面友好性
      • 2.用高级 Hook-useAsync 统一处理 Loading 和 Error 状态


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

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


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

  • 【实战】 一、项目起航:项目初始化与配置 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(一)

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

  • 【实战】 二、React 与 Hook 应用:实现项目列表 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二)

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

  • 【实战】三、 TS 应用:JS神助攻 - 强类型 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(三)

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

  • 【实战】四、 JWT、用户认证与异步请求(上) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(四)

  • 【实战】四、 JWT、用户认证与异步请求(下) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(五)

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

  • 【实战】 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(六)

  • 【实战】 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(七)

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

1.给页面添加 Loading 和 Error 状态,增加页面友好性

修改 src\screens\ProjectList\index.tsx(新增 loading 状态 和 请求错误提示)(部分未修改内容省略):

...
import { Typography } from "antd";export const ProjectList = () => {const [users, setUsers] = useState([]);const [isLoading, setIsLoading] = useState(false);const [error, setError] = useState<null | Error>(null);...useEffect(() => {setIsLoading(true)// React Hook "useHttp" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.client("projects", { data: cleanObject(lastParam) }).then(setList).catch(error => {setList([])setError(error)}).finally(() => setIsLoading(false));// React Hook useEffect has a missing dependency: 'client'. Either include it or remove the dependency array.// eslint-disable-next-line react-hooks/exhaustive-deps}, [lastParam]);...return (<Container><h1>项目列表</h1><SearchPanel users={users} param={param} setParam={setParam} />{error ? <Typography.Text type="danger">{error.message}</Typography.Text> : null}<List loading={isLoading} users={users} dataSource={list} /></Container>);
};...

修改 src\screens\ProjectList\components\List.tsxListProps 继承 TableProps, Table 的属性(透传))(部分未修改内容省略):

import { Table, TableProps } from "antd";
...interface ListProps extends TableProps<Project> {users: User[];
}// type PropsType = Omit<ListProps, 'users'>
export const List = ({ users, ...props }: ListProps) => {return (<Tablepagination={false}columns={...}{ ...props }></Table>);
};

为方便后续在组件外再次配置 Table 的属性(透传),直接让 ListProps 继承 TableProps, TableProps 单独抽出到 props

配置请求最短时间(如下图),即可清楚看到 loading 效果
在这里插入图片描述
配置请求失败比例为百分百即可看到错误提示:
在这里插入图片描述

2.用高级 Hook-useAsync 统一处理 Loading 和 Error 状态

新建 src\utils\use-async.ts (统一对 异步状态请求数据 的管理):

import { useState } from "react";interface State<D> {error: Error | null;data: D | null;stat: 'ready' | 'loading' | 'error' | 'success'
}const defaultInitialState: State<null> = {stat: 'ready',data: null,error: null
}export const useAsync = <D>(initialState?: State<D>) => {const [state, setState] = useState<State<D>>({...defaultInitialState,...initialState})const setData = (data: D) => setState({data,stat: 'success',error: null})const setError = (error: Error) => setState({error,stat: 'error',data: null})// run 来触发异步请求const run = (promise: Promise<D>) => {if(!promise || !promise.then) {throw new Error('请传入 Promise 类型数据')}setState({...state, stat: 'loading'})return promise.then(data => {setData(data)return data}).catch(error => {setError(error)return error})}return {isReady: state.stat === 'ready',isLoading: state.stat === 'loading',isError: state.stat === 'error',isSuccess: state.stat === 'success',run,setData,setError,...state}
}

修改 src\screens\ProjectList\components\List.tsx (将 Project 导出,以便后续引用)(部分未修改内容省略):

...
export interface Project {...}
...

修改 src\screens\ProjectList\index.tsx (部分未修改内容省略):

  • 删去之前 loadingerror 相关内容;
  • 删去 client 异步请求 then 及后续操作;
  • 使用 useAsync 统一处理 异步状态请求数据
...
import { List, Project } from "./components/List";
...
import { useAsync } from "utils/use-async";export const ProjectList = () => {const [users, setUsers] = useState([]);const [param, setParam] = useState({name: "",personId: "",});// 对 param 进行防抖处理const lastParam = useDebounce(param);const client = useHttp();const { run, isLoading, error, data: list } = useAsync<Project[]>();useEffect(() => {run(client("projects", { data: cleanObject(lastParam) }))// eslint-disable-next-line react-hooks/exhaustive-deps}, [lastParam]);useMount(() => client("users").then(setUsers));return (<Container>...<List loading={isLoading} users={users} dataSource={list || []} /></Container>);
};
...

新建 src\utils\project.ts (单独处理 Project 数据的异步请求):

import { cleanObject } from "utils";
import { useHttp } from "./http";
import { useAsync } from "./use-async";
import { useEffect } from "react";
import { Project } from "screens/ProjectList/components/List";export const useProjects = (param?: Partial<Project>) => {const client = useHttp();const { run, ...result } = useAsync<Project[]>();useEffect(() => {run(client("projects", { data: cleanObject(param || {}) }))// eslint-disable-next-line react-hooks/exhaustive-deps}, [param]);return result
}

新建 src\utils\use-users.ts (单独处理 User 数据的异步请求):

import { cleanObject } from "utils";
import { useHttp } from "./http";
import { useAsync } from "./use-async";
import { useEffect } from "react";
import { User } from "screens/ProjectList/components/SearchPanel";export const useUsers = (param?: Partial<User>) => {const client = useHttp();const { run, ...result } = useAsync<User[]>();useEffect(() => {run(client("users", { data: cleanObject(param || {}) }))// eslint-disable-next-line react-hooks/exhaustive-deps}, [param]);return result
}

再次修改 src\screens\ProjectList\index.tsx (部分未修改内容省略):

  • ProjectUser 数据获取分别单独抽离
import { SearchPanel } from "./components/SearchPanel";
import { List } from "./components/List";
import { useState } from "react";
import { useDebounce } from "utils";
import styled from "@emotion/styled";
import { Typography } from "antd";
import { useProjects } from "utils/project";
import { useUsers } from "utils/use-users";export const ProjectList = () => {const [param, setParam] = useState({name: "",personId: "",});// 对 param 进行防抖处理后接入请求const { isLoading, error, data: list } = useProjects(useDebounce(param));const { data: users } = useUsers();return (<Container><h1>项目列表</h1><SearchPanel users={users || []} param={param} setParam={setParam} />{error ? (<Typography.Text type="danger">{error.message}</Typography.Text>) : null}<List loading={isLoading} users={users || []} dataSource={list || []} /></Container>);
};
...

测试功能:一切正常!


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

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

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

相关文章

Observability:Synthetic monitoring - 合成监测入门

从我们的全球测试基础设施监控关键用户旅程&#xff0c;并了解网络和前端性能对用户体验的影响。 全面了解你的网站性能、功能和可用性&#xff08;从开发到生产&#xff09;&#xff0c;并在客户之前发现问题。合成监测&#xff08;synthetic monitoring&#xff09;使你能够模…

SAP 销售订单状态修改

FM: I_CHANGE_STATUS 代码样例&#xff1a; FUNCTION zjkdms003. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" IMPORTING *" VALUE(I_VBELN) TYPE VBELN_VA OPTIONAL *" EX…

解决Python的SyntaxError: Non-UTF-8 code starting with ‘\xbb‘问题

文章目录 一、报错二、分析三、解决3.1 方法一3.2 方法二3.3 方法三 一、报错 在程序中&#xff0c;line 8为含有中文的注释&#xff0c;编译后出现了SyntaxError: Non-UTF-8 code starting with \xbb的报错&#xff0c;具体如下图。 二、分析 这个错误是由于Python解释器无…

nacos身份认证绕过漏洞

1.影响范围 Nacos < 2.0.0-ALPHA.1 2.验证漏洞是否存在 http://example/nacos/v1/auth/users/?pageNo1&pageSize5 如果列出了用户名密码,即证明此漏洞存在 3.向系统中添加一个新的用户 http://example/nacos/v1/auth/users/?usernamesectest&passwordsectest…

阿里前端常考vue面试题汇总

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 Vuex中actions和mutations有什么区别 题目分析 mutations和actions是vuex带来的两个独特…

Spring Boot 中的分布式配置中心:什么是它,原理及如何使用

Spring Boot 中的分布式配置中心&#xff1a;什么是它&#xff0c;原理及如何使用 在分布式应用程序中&#xff0c;配置管理是一项重要的任务。通常情况下&#xff0c;配置信息被硬编码在应用程序中&#xff0c;这样一来&#xff0c;当应用程序需要相应的配置信息时&#xff0…

力扣 40. 组合总和 II

题目来源&#xff1a;https://leetcode.cn/problems/combination-sum-ii/description/ C题解&#xff1a; 这道题的难点在于解集中不能包含重复的组合。如果用set去重会造成超时&#xff0c;所以只能在单层递归逻辑中处理。通过识别下一个数与当前数是否相同&#xff0c;来修改…

【软件设计师暴击考点】网络安全等杂项高频考点暴击系列

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…

Texlive2023安装过程中点击install-tl-windows.bat一直闪退

我安装了Texlive的镜像&#xff0c;因为数学建模会用到&#xff0c;在安装过程中我遇到了一个问题&#xff1a; install-tl-windows.bat一直闪退 当我解压完texlive.iso的时候,正常应该点击.bat文件就能出现页面&#xff0c;但是我cmd窗口一直闪退 怎样解决&#xff1f; 这个…

Flutter 布局构建

文章目录 一、布局类组件简介二、理解 Flutter 布局约束三、线性布局&#xff08;Row和Column&#xff09;1. 主轴和纵轴2. Row3. Column4. 特殊情况 四、弹性布局&#xff08;Flex 和 Expanded&#xff09;1. Flex2. Expanded 五、流式布局&#xff08;Wrap 和 Flow&#xff0…

运动戴耳机哪种款式比较好、运动型蓝牙耳机推荐

在当今的快节奏生活中&#xff0c;运动已经成为许多人保持健康和放松身心的重要途径。而一款出色的运动耳机&#xff0c;不仅可以给我们带来激励和动力&#xff0c;还能提供优质的音乐体验&#xff0c;让我们在运动时全身心地享受音乐的愉悦。 然而&#xff0c;对于运动者来说…

100天精通Python(可视化篇)——第94天:Pyecharts绘制多种炫酷散点图(参数说明+代码实战)

文章目录 专栏导读一、Scatter&#xff08;散点图&#xff09;1. add函数2. 数据项 二、基础气泡图三、多维度散点图四、添加分割线五、动态涟漪散点图六、不同形状散点图七、3D散点图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#…