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

最后的话

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

资料预览

给大家整理的视频资料:

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

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

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

需要这份系统化的资料的朋友,可以点击这里获取!

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

四、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>
  );
};
...

检验一下,没有效果,但是控制台抛出 400 错误了,排查一下

  • try..catch 中的 onError 没接收到,唯一的变数就是这个 run
  • 查看一下,果然报错被 run 内部消化了,没有正常抛出(将 catch 到的 error throw 或是用 Promise.reject 包裹返回都是可以的,建议使用后者)

修改 src\utils\use-async.ts

...
export const useAsync = <D>(initialState?: State<D>) => {
  ...
  // run 来触发异步请求
  const run = (promise: Promise<D>) => {
    ...
    return promise
      .then(...)
      .catch((error) => {
        // catch 会消化异常,如果不主动抛出,外面是接收不到异常的
        setError(error);
        // return error; // 原代码
        // throw error;
        return Promise.reject(error);
      });
  };
  ...
};

检验一下,正常 catch 并 展示报错信息

  • try…catch only works for runtime errors (try…catch 只能处理有效代码之中的异常)
  • try…catch works synchronously(try…catch 只能处理同步代码之中的异常)

问题是解决了,但这样 try…catch 还是有些拖泥带水的感觉,继续优化:

修改 src\utils\use-async.ts(增加是否抛出异常的配置,来合理化逻辑):

...
const defaultConfig = {
  throwOnError: false
}

export const useAsync = <D>(initialState?: State<D>, initialConfig?: typeof defaultConfig) => {
  const config = {...defaultConfig, ...initialConfig}
  ...

  // run 来触发异步请求
  const run = (promise: Promise<D>) => {
    ...
    return promise
      .then((data) => {
        setData(data);
        return data;
      })
      .catch((error) => {
        // catch 会消化异常,如果不主动抛出,外面是接收不到异常的
        setError(error);
        return config.throwOnError ? Promise.reject(error) : error;
      });
  };
  ...
};

修改 src\unauthenticated-app\login.tsx (传入 { throwOnError: true }):

...
export const Login = ({onError}: { onError: (error: Error) => void }) => {
  ...
  const { run, isLoading } = useAsync(undefined, { throwOnError: true })


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


最全的Linux教程,Linux从入门到精通

======================

1.  **linux从入门到精通(第2版)**

2.  **Linux系统移植**

3.  **Linux驱动开发入门与实战**

4.  **LINUX 系统移植 第2版**

5.  **Linux开源网络全栈详解 从DPDK到OpenFlow**



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/59742364bb1338737fe2d315a9e2ec54.png)



第一份《Linux从入门到精通》466页

====================

内容简介

====

本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。



![华为18级工程师呕心沥血撰写3000页Linux学习笔记教程](https://img-blog.csdnimg.cn/img_convert/9d4aefb6a92edea27b825e59aa1f2c54.png)



**本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。**

> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论




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

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

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

a27b825e59aa1f2c54.png)



**本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。**

> 需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论




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

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

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

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值