文章目录
- 一、项目起航:项目初始化与配置
- 二、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<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
点击“抛出异常”按钮:
- 测试环境:页面展示抛出异常
- 生产环境:页面不变,控制台抛出异常
登录后:
- 测试环境:页面展示异常信息
- 生产环境:页面空白,控制台打印出异常信息
这两种异常对比可看出:在渲染阶段出现未被捕获的异常,整个组件树都会被卸载(错误的展示内容比空白内容更可怕)
- 错误边界 – 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.PropsWithChildren是React中的一个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
部分引用笔记还在草稿阶段,敬请期待。。。






![NSS [SWPUCTF 2022 新生赛]funny_web](https://img-blog.csdnimg.cn/img_convert/18e9c3234ecb30404dcaf2c6b816cc79.png)













