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


学习内容来源: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 应用:实现项目列表

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

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


五、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<User | null>(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

点击“抛出异常”按钮:

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

登录后:

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

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

接下来写一个错误边界捕获组件 —— 新建: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 = unknown> = 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:

在这里插入图片描述

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_32682301/article/details/131624198