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

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
      • 1~2
      • 3
      • 4.用useAsync获取用户信息
      • 5.实现 Error Boundaries,捕获边界错误


学习内容来源: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
prettier 2.8.4
json-server 0.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 添加样式(下)

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

1~2

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

3

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

4.用useAsync获取用户信息

修改 src\components\lib.tsx(新增全屏 Loading 组件 和 全屏 Error 展示组件):

import { Spin, Typography } from "antd";
import { DevTools } from "jira-dev-tool";

...
const FullPage = styled.div`
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
`

export const FullPageLoading = () => <FullPage>
  <Spin size="large"/>
</FullPage>

export const FullPageErrorFallback = ({error}: {error: Error | null}) => <FullPage>
  <DevTools/>
  <Typography.Text type="danger">{error?.message}</Typography.Text>
</FullPage>
  • 为了展示报错信息的同时,DevTools 依旧展示,需要引入

修改 src\context\auth-context.tsx(使用 useAsync 改造,并新增全屏 Loading 组件 和 全屏 Error 展示组件)(部分未修改内容省略):

...
import { useAsync } from "utils/use-async";
import { FullPageErrorFallback, FullPageLoading } from "components/lib";

...

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  // 这里要考虑到初始值的类型与后续值类型,取并组成一个泛型
  // const [user, setUser] = useState(null);
  const { data: user, error, isLoading, isReady, isSuccess, isError, run, setData: setUser } = useAsync<User | null>()

  ...

  useMount(() => run(initUser()));

  if (isReady || isLoading) {
    return <FullPageLoading/>
  }

  if (isError) {
    return <FullPageErrorFallback error={error}/>
  }

  return (...);
};
...

查看效果:完美!

5.实现 Error Boundaries,捕获边界错误

修改 src\unauthenticated-app\index.tsx(新增一个“抛出异常”按钮):

...
export const UnauthenticatedApp = () => {
  ...
  return (
    <Container>
      <Header />
      <Background />
      <Button onClick={() => {
        throw new Error('点击抛出一个异常')
      }}>抛出异常</Button>
      <ShadowCard>...</ShadowCard>
    </Container>
  );
};
...

修改 src\authenticated-app.tsx(新增一个变量展示它不存在的一个属性):

...
export const AuthenticatedApp = () => {
  ...
  const value: any = undefined;
  ...
  return (
    <Container>
      { value.notExist }
      ...
    </Container>
  );
};
...

编译代码并全局安装推荐的 serve 库,然后启动并访问:

npm run build
yarn global add serve
serve -s build

点击“抛出异常”按钮:

  • 测试环境:页面展示抛出异常
  • 生产环境:页面不变,控制台抛出异常

登录后:

  • 测试环境:页面展示异常信息
  • 生产环境:页面空白,控制台打印出异常信息

这两种异常对比可看出:在渲染阶段出现未被捕获的异常,整个组件树都会被卸载(错误的展示内容比空白内容更可怕)

  • 错误边界 – React

接下来写一个错误边界捕获组件 —— 新建:src\components\error-boundary.tsx

import React, { ReactNode } from "react";

type FallbackRender = (props: { error: Error | null }) => React.ReactElement

// children: ReactNode
export class ErrorBoundary extends React.Component<React.PropsWithChildren<{fallbackRender: FallbackRender}>, { error: Error | null }> {
  state = { error: null }

  // 当子组件抛出异常,这里会接受到并更改 state
  static getDerivedStateFromError(error: Error) {
    return { error }
  }

  render() {
    const { error } = this.state
    const { fallbackRender, children } = this.props
    return error ? fallbackRender({ error }) : children
  }
}
  • 如果一个 class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界
  • React.PropsWithChildrenReact 中的一个 Utility Types (工具类型) 类型处理器,将传入属性以类似 Object.assign 的方式合并:
    • type PropsWithChildren

      = P & { children?: ReactNode | undefined };

修改:src\App.tsx(使用错误边界组件 ErrorBoundary 包裹,并将异常展示在 FullPageErrorFallback 中):

...
import { ErrorBoundary } from "components/error-boundary";
import { FullPageErrorFallback } from "components/lib";

function App() {
  ...
  return (
    <div className="App">
      <ErrorBoundary fallbackRender={FullPageErrorFallback}>
       {user ? <AuthenticatedApp /> : <UnauthenticatedApp />}
      </ErrorBoundary>
    </div>
  );
}
...

重新编译代码并重启serve,然后访问:

npm run build
serve -s build

手动抛出错误还是原样,渲染异常导致的边界错误被截获并展示!

Cannot read property 'notExist' of undefined

测试过程中可能会需要清除 localStorage:

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

测试结束后清除以下两个文件中的测试内容(“抛出异常”按钮 和 “value”):

  • src\unauthenticated-app\index.tsx
  • src\authenticated-app.tsx

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

你可能感兴趣的:(ux,react.js,前端)