【实战】 六、用户体验优化 - 加载中和错误状态处理(中) —— React17


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

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

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


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


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

1~2
3.登录注册页面 Loading 和 Error 状态处理,与 Event Loop 详解

列表页的 异步状态 弄完,接下来是登录注册页了

修改 src\unauthenticated-app\index.tsx(新增 error 状态处理,将 error j监听操作 交给 登录注册页):

...
import { Card, Button, Divider, Typography } from "antd";
...

export const UnauthenticatedApp = () => {
  ...
  const [error, setError] = useState<Error | null>(null);
  return (
    <Container>
      ...
      <ShadowCard>
        <Title>{isRegister ? "请注册" : "请登录"}</Title>
        { error ? <Typography.Text type="danger">{error.message}</Typography.Text> : null }
        {isRegister ? <Register onError={setError}/> : <Login onError={setError}/>}
        <Divider />
        ...
      </ShadowCard>
    </Container>
  );
};
...

修改 src\unauthenticated-app\login.tsx(传入 onError 并在异步操作后 catch 中使用):

...
export const Login = ({onError}: { onError: (error: Error) => void }) => {
  ...
  const handleSubmit = (values: { username: string; password: string }) => {
    login(values).catch(e => onError(e))
  };
  ...
};
...

同理修改 src\unauthenticated-app\register.tsx

...
export const Register = ({onError}: { onError: (error: Error) => void }) => {
  ...
  const handleSubmit = (values: { username: string; password: string }) => {
    register(values).catch(e => onError(e))
  };
  ...
};
...

使用非预设用户名密码检验:没反应。。。但是控制台打印出了刚输入的用户名和密码。。。

通过登录的调用链可以找到 导致这个问题的原因:src\auth-provider.ts

  • !res.ok 时,返回了 Promise.reject(data) ,而 data 是请求入参,这显然不是预想的效果(注册同理),修改这部分为 Promise.reject(await res.json())

修改后再次检验,成了!

Promise.catch 固然好用,但接下来换个思路,使用 try..catch 并引出 Event Loop

先修改 src\unauthenticated-app\login.tsx 试试水:

...
export const Login = ({onError}: { onError: (error: Error) => void }) => {
  ...
  const handleSubmit = (values: { username: string; password: string }) => {
    try {
      // login(values).catch(e => onError(e))
      login(values);
    } catch(e: Error | any) {
      onError(e)
    }
  };
  ...
};
...

控制台输出正常,但是界面没有效果。。。

问题出在 login 是异步操作,程序中会优先执行同步操作,然后才会异步操作,所以 onError 优先执行,并没有拿到后端返回的报错信息

再次修改 src\unauthenticated-app\login.tsx (使用 async await 处理异步操作):

...
export const Login = ({onError}: { onError: (error: Error) => void }) => {
  ...
  const handleSubmit = async (values: { username: string; password: string }) => {
    try {
      // login(values).catch(e => onError(e))
      await login(values);
    } catch(e: Error | any) {
      onError(e)
    }
  };
  ...
};
...

这样便正常啦!

接下来给注册页新增确认密码功能

修改 src\unauthenticated-app\register.tsx (新增确认密码的 Form.Item 和 相关处理逻辑):

...
export const Register = ({onError}: { onError: (error: Error) => void }) => {
  const { register, user } = useAuth();
  const handleSubmit = ({ cpassword, ...values }: { username: string, password: string, cpassword: string }) => {
    if (cpassword === values.password) {
      register(values).catch(e => onError(e));
    } else {
      onError(new Error('请确认两次的输入密码相同'))
      return
    }
  };
  return (
    <Form onFinish={handleSubmit}>
      <Form.Item
        name="username"
        rules={[{ required: true, message: "请输入用户名" }]}
      >
        <Input placeholder="用户名" type="text" id="username" />
      </Form.Item>
      <Form.Item
        name="password"
        rules={[{ required: true, message: "请输入密码" }]}
      >
        <Input placeholder="密码" type="password" id="password" />
      </Form.Item>
      <Form.Item
        name="cpassword"
        rules={[{ required: true, message: "请确认密码" }]}
      >
        <Input placeholder="确认密码" type="password" id="cpassword" />
      </Form.Item>
      <Form.Item>
        <LongButton htmlType="submit" type="primary">
          注册
        </LongButton>
      </Form.Item>
    </Form>
  );
};

再接着为 登录注册页 添加异步状态 Loading 的处理:

...
import { useAsync } from "utils/use-async";

export const Login = ({onError}: { onError: (error: Error) => void }) => {
  const { login, user } = useAuth();
  const { run, isLoading } = useAsync()

  const handleSubmit = async (values: { username: string; password: string }) => {
    try {
      // login(values).catch(e => onError(e))
      await run(login(values))
    } catch(e: Error | any) {
      onError(e)
    }
  };
  return (
    <Form onFinish={handleSubmit}>
      ...
      <Form.Item>
        <LongButton loading={isLoading} htmlType="submit" type="primary">
          登录
        </LongButton>
      </Form.Item>
    </Form>
  );
};
...



### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/6bb6d2236b7e959a1de5dcdcbdea0c3f.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/994c2f8cb14c2eacce209d7a4507c657.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-kQMZgcNJ-1714326235150)]

给大家整理的电子书资料:

  

[外链图片转存中...(img-UrCa3Fdq-1714326235151)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值