之前已经实现的内容:
至上一篇为止,注册的基本功能已经实现了,但是用户注册了之后 UI 没有与用户实现有意义的互动,并且当用户回到注册页面的时候,基本的注册信息被持久化在页面上。
这一篇主要实现的目的就是对这些功能进行优化。
注册功能的优化
一般页面注册流程如下:
重置状态的目的是为了清空:
- 注册成功信息
- 注册失败信息
- 用户注册信息
这样,当用户下一次打开注册页面时,之前提示的所有信息都会被重置。
获取注册状态
回顾一下在上一篇 注册功能的实现 中,注册功能的状态已经被定义好了:
// Auth状态中管理注册的逻辑
// loaded代表API是否调用成功
// success代表是否注册成功
// message代表的是错误信息
export interface AuthState {
signup: {
loaded: boolean;
success: boolean;
message: string;
};
}
// 根据interface去初始化状态
const initialState: AuthState = {
signup: {
loaded: false,
success: false,
message: '',
},
};
也就是说,只要能够获取注册的 store,就可以获取注册的状态。React Redux 封装了一个勾子函数 useSelector
,通过 useSelector
就可以很方便的获取 React Redux 中的状态:
在 Signup.txs
中新增如下代码获取注册的状态:
// ...省略 import
const Signup = () => {
// 获取 auth的状态
// AppState, AuthState 是为了满足 TypeScript 中静态检查的需求
// 如果不传值,TypeScript 会 complain 说
// Property 'auto' does not exist on the 'DefaultRootState'
const auth = useSelector<AppState, AuthState>((state) => state.auth);
// ... 省略未修改代码
};
接下来注册页面的状态都可以通过 auth
去获得。
注册流程的优化
这一步会根据流程图去进行四个优化功能的实现:
- 清空表单
- 显示成功提示信息
- 显示失败提示信息
- 重置状态
而这些功能都需要通过监听 auth
状态的更改去实现,React 自身封装了 useEffect
生命周期钩子函数,可以监听状态变更从而进行 DOM 的操作。其功能类似于 class-based component 中的 componentDidUpdate()
生命周期方法。
useEffect
结构如下:
// ...省略 import
const Signup = () => {
useEffect(() => {
// 触发 effect
effect;
// 返回一个函数去做清理副作用
return () => {
cleanup;
};
// 第二个参数会监听值的变化,如这里就会监听 `input` 的变化
}, [input]);
};
通过 useEffect
就可以去监听 auth
的变化,从而获悉登陆状态的变更。
清空表单
表单组件是 antd 提供的,表单状态的修改最简单的方式也是通过 antd 去管理,antd 的 Form
组件提供了 useForm
钩子函数去对表单的状态进行管理:
const Signup = () => {
// antd官方用法
const [form] = Form.useForm();
// 通过 useEffect 去同时监听
useEffect(() => {
if (auth.signup.loaded && auth.signup.success) {
form.resetFields();
}
// 监听 auth 状态的变化去进行表单重置
}, [auth]);
// 同时需要将 form 传入 Form 组件,使得 Form 组件知道状态的变更
return (
<Layout title="注册" subTitle={
''}>
<Form form={
form} onFinish={
onFinish}>
{
/* 忽略其他 */}
</Form>
</Layout>
);
};
显示成功提示信息
显示登录成功提示信息可以通过调用函数进行处理,这样不需要通过 useState
重新定义一个状态,也不需要在 useEffect
中进行副作用的管理:
const Signup = () => {
const showSuccess = () => {
if (auth.signup.loaded && auth.signup.success) {
return (
<Result
key="signup-success"
status="success"
title="注册成功"
extra={
[
<Button type="primary">
<Link to="/signin">登录</Link>
</Button>,
]}
/>
);
}
};
return (
<Layout title="注册" subTitle={
''}>
{
showSuccess()}
<Form form={
form} onFinish={
onFinish}>
{
/* 忽略其他 */}
</Form>
</Layout>
);
};
登录成功与表单重置的 Demo:
显示失败提示信息
失败的提示信息也可以通过同样的方法进行实现:
const Signup = () => {
const showError = () => {
if (auth.signup.loaded && !auth.signup.success) {
return (
<Result
key="signup-fail"
status="warning"
title="注册失败"
subTitle={
auth.signup.message}
/>
);
}
};
return (
<Layout title="注册" subTitle={
''}>
{
showSuccess()}
{
showError()}
<Form form={
form} onFinish={
onFinish}>
{
/* 忽略其他 */}
</Form>
</Layout>
);
};
showSuccess
与 showError
具有排他性,二者只会渲染一个。并且 JavaScript 函数默认返回 undefined,没有进入 if
控制流程的话就会直接返回 undefined。
注册失败的 Demo:
重置状态
状态重置需要在组件卸载时使用,在 class-based component 中,可以使用 componentdidunmount
进行实现。在 functional component 中,同样可以通过 useEffect
去实现。
useEffect
第二个参数为空数组时,就会在组件 第一次渲染 以及 即将卸载 时进行调用,类似于 ComponentDidMount()
与 componentWillUnmount()
。
const Signup = () => {
// 省略其他
useEffect(() => {
// 如果这里有其他函数,将会在组件第一次渲染时调用
// return 中的方法会在组件卸载时调用
return () => {
// 依旧使用 Redux 进行管理
dispatch(resetSignup());
};
}, []);
// 省略渲染部分
};
Redux 的流程与之前登录的功能类似。
-
action
export interface SignupResetAction { type: typeof SIGNUP_RESET; } export const resetSignup = () => ({ type: SIGNUP_RESET, });
-
reducer
export default function authReducer(
state = initialState,
action: AuthUnionType
) {
switch (
action.type
// 省略其他
case SIGNUP_RESET:
return {
...state,
// 重置成初始状态
signup: {
loaded: false,
success: false,
message: "",
},
};
) {
}
}
注册重置的 Demo:
鉴于这里使用的匿名模式,React Dev Tool 和 Redux Dev Tool 在匿名模式下不工作,所以这里通过检查 Redux Logger 去查看状态的变化。
可以看到,在 Redux Logger 中,prev state
中的 signup 的数据结构为:
signup: {
loaded: true,
success: true
}
而在 next state
中的 signup 的数据结构为:
signup: {
loaded: false,
success: false,
message: ""
}
至此,注册的功能就完成了,下一篇会实现登录的功能。