今日目标
✔ 掌握登录的完整流程。
✔ 表单数据校验和收集、Axios 封装、Token 持久化、发送验证码等。
登录界面
目标
能够手动搭建登录界面的效果。
步骤
- 完成登录界面的导航栏,NavBar 组件。
1 | import styles from './index.module.scss' |
- 添加登录表单,Form 组件。
1 | import styles from './index.module.scss' |
login/index.module.scss
1 | // 导入 1px 边框的样式文件 |
注意:上面是基于 antd-mobile@5.0.0-rc.0
的版本进行的样式调整,新版本或许会有变化。
表单校验
目标
能够为登录表单添加校验。
代码
login/index.tsx
,给 Form.Item 组件添加 name 和 rules 属性,name 的值可以参考接口文档。
1 | import styles from './index.module.scss' |
收集数据
目标
能够拿到手机号和验证码数据。
步骤
为 Form 表单添加 onFinish 事件。
创建 onFinish 函数,作为 Form 属性 onFinish 事件的回调。
指定函数 onFinish 的参数类型。
通过参数获取到表单数据。
代码
login/index.tsx
1 | import styles from './index.module.scss' |
axios 封装
- 安装 axios。
1 | yarn add axios |
utils/request.ts
。
1 | import axios from 'axios' |
跑通 Redux
目标
能够初始化 Redux。
步骤
- 安装 Redux 相关的包。
1 | yarn add redux react-redux redux-thunk@2.3.0 redux-devtools-extension |
- 创建
reducers/login.ts
文件,创建基础 login reducer 并导出。
1 | const initialState = {} |
- 创建
reducers/index.ts
文件,创建 rootReducer 并导出。
1 | import { combineReducers } from 'redux' |
- 在
store/index.ts
中,创建 store 并导出。
1 | import { applyMiddleware, createStore } from 'redux' |
- 在
src/index.tsx
中为 React 组件接入 Redux。
1 | import ReactDOM from 'react-dom' |
配置 Redux 相关类型
目标
能够配置 Redux 的基础类型。
步骤
在 types 目录中创建两个类型声明文件:
store.d.ts
和data.d.ts
。store.d.ts
:用来存放跟 Redux 相关类型,比如,action 的类型等。data.d.ts
:存放各种通用的数据类型,一般是跟数据接口相关的。
代码
src/types/store.d.ts
。
1 | // 存放和 Redux 相关的所有类型,RootState、RootAction、RootThunkAction。 |
src/types/data.d.ts
。
1 | // 存放各种通用的数据,pages/Login/index.tsx 中的 LoginForm 就可以引用这个啦 |
src/store/actions/login.ts
,定义登录的 Action。
1 | import { LoginForm } from '@/types/data' |
发送登录请求
目标
能够在 Redux 中实现登录逻辑。
步骤
在
store/actions
中创建login.ts
文件。创建 login 函数并导出。
在函数中根据接口发送请求实现登录功能。
代码
store/actions/login.ts
1 | import { LoginForm } from '@/types/data' |
pages/Login/index.tsx
1 | import styles from './index.module.scss' |
处理 Axios 响应类型
基本处理
src/types/data.d.ts
1 | export type LoginForm = { mobile: string; code: string } |
src/store/actions/login.ts
1 | import { LoginForm, Token } from '@/types/data' |
继续封装
types/data.d.ts
1 | export type LoginForm = { mobile: string; code: string } |
actions/login.ts
1 | import { ApiResponse, LoginForm, Token } from '@/types/data' |
存储到 Redux
types/store.d.ts
,先添加下 LoginAction 的 payload 类型。
1 | import store from '@/store' |
store/actions/login.ts
。
1 | import { ApiResponse, LoginForm, Token } from '@/types/data' |
store/reducers/login.ts
。
1 | import { Token } from '@/types/data' |
登录失败后处理
pages/Login/index.tsx
1 | const onFinish = async (values: LoginForm) => { |
解决报错。
1 | const onFinish = async (values: LoginForm) => { |
但是输入 e.response.data.message
的时候没有提示,可能会思考给 e 加上 axios 的错误类型,如下。
1 | const onFinish = async (values: LoginForm) => { |
解决:类型断言。
1 | const onFinish = async (values: LoginForm) => { |
问题:data.message 没有提示,解决如下。
1 | const onFinish = async (values: LoginForm) => { |
统一错误处理
src/utils/request.ts
1 | request.interceptors.response.use( |
登录成功后跳转
pages/Login/index.tsx
1 | const onFinish = async (values: LoginForm) => { |
持久化 Token
函数封装
src/utils/storage.ts
1 | import { Token } from '@/types/data' |
存储到本地并获取
登录成功时存储 Token 到本地。
在
store/reducers/login.ts
中获取 Token。
store/actions/login.ts
1 | import { ApiResponse, LoginForm, Token } from '@/types/data' |
store/reducers/login.ts
1 | import { Token } from '@/types/data' |
发送验证码
校验手机号
目标
能够实现点击发送验证码时获取到手机号码,参考文档。
步骤
给发送验证码绑定点击事件。
在点击事件中获取到文本框的值。
判断文本框的值是否为空。
如果为空或手机号格式错误时,让文本框自动获得焦点。
代码
pages/Login/index.tsx
1 | import styles from './index.module.scss' |
发送请求
目标
能够使用 Redux 发送请求获取验证码。
步骤
在 Login 组件中导入获取验证码的 action。
在获取验证码事件中分发获取验证码的 action。
在 login action 中创建获取验证码的 action 并导出。
发送请求获取验证码。
代码
store/actions/login.ts
1 | import { ApiResponse, LoginForm, Token } from '@/types/data' |
pages/Login/index.tsx
1 | const onGetCode = () => { |
开启倒计时
目标
能够在点击获取验证码时显示倒计时。
步骤
创建状态 timeLeft 倒计时数据。
在点击获取验证码的事件处理程序中,更新倒计时时间并开启定时器。
在定时器中,更新状态(需要使用回调函数形式的 setTimeLeft)。
在开启定时器时,展示倒计时时间。
代码
pages/Login/index.tsx
1 | import styles from './index.module.scss' |
获取最新 time 值
1 | setInterval(() => { |
清理定时器
倒计时结束清理定时器
通过 useRef Hook 创建一个 ref 对象,用来存储定时器 id。
在开启定时器时,将定时器 id 存储到 ref 对象中。
通过 useEffect Hook 监听倒计时的变化。
判断倒计时时间是否为 0 ,如果为 0 就清理定时器。
1 | import styles from './index.module.scss' |
其他问题处理
组件销毁的时候清理定时器
问题:倒计时期间,登录成功后会报错。
1 | // 单独写一个 useEffect |
倒计时的时候再点击不要做处理
1 | const onGetCode = () => { |