[React 实战系列] 注册功能的实现
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[React 实战系列] 注册功能的实现相关的知识,希望对你有一定的参考价值。
[React 实战系列] 注册功能的实现
之前已经实现的内容:
注册功能的实现
注册的页面 UI 既然已经实现了,剩下的就是向里面填充功能。注册的功能会使用 Redux 去进行规范化,所以,首先需要时间 Redux 的 Action 和 Reducer。
注册的 Redux
回顾一下 Redux 的核心逻辑就是这样的一个轮回:
因此,当前的实现是一旦 UI 上的点击事件被触发,就在 action 中异步调用 AJAX,随后通过 reducer 去将用户信息更新到 store/local storage 中。因为涉及到了异步调用,所以这里会使用 Redux Saga 去进行异步操作。
注册页面的 Action
新建注册页面所需的文件,其结构为 store > actions > auth.action.ts
,此时的 Redux 结构为:
|- store
| |- actions
| | |- auth.action.ts
| |- reducers
| | |- ... # 其余文件省略
接下来的实现中,首先会使用 const 去定义每一个 action 的 type,随后会使用 TypeScript 对 Redux 进行规范化。这里会使用 interface 去定义每一个 action 会接受的 payload,再使用 interface 去规定每一个 action 所要接收的参数。
注册这个业务场景存在三种 actions 可能会发生:
- 注册本身
- 注册成功
- 注册失败
这里也会将内容拆分成针对每一个业务的功能区实现。
在开始正式编写 actions 之前,先复习一下 action 的基本返回类型:
action = {
type: 'action type in string',
payload: {
payloadObj: payloadVal,
},
};
action 的返回值必须包含的值是 type
,reducer 可以通过 action type 去执行正确的操作。payload 则是传给 reducer 的值,大多数情况下 reducer 从传进来的 payload 中获取需要更新的值。
注册业务
首先是 action type 的实现:
export const SIGNUP = 'SIGNUP';
随后就需要使用 TypeScript 对 action 以及 payload 进行定义。
首先,是 action 自己本身接受的 参数(payload) 的定义。根据页面的表单,注册需要的是邮箱地址、姓名和密码三个条件。随后,再定义 action 自己自身的定义。
具体实现如下:
export interface SignupPayload {
email: string;
name: string;
password: string;
}
export interface SignupAction {
type: typeof SIGNUP;
payload: SignupPayload;
}
有了定义,就可以对 action 进行实现了:
// signup 这个 action 会接受一个参数,这个参数的类型就是 SignupPayload
export const signup = (payload: SignupPayload) => ({
type: SIGNUP,
payload,
});
注册成功
注册成功的 action 不需要 payload 这个值,只需要 type 类型以及成功的 action 即可。具体实现如下:
export const SIGNUP_SUCCESS = 'SIGNUP_SUCCESS';
// 需要定义成功的action的数据类型
export interface SignupSuccessAction {
type: typeof SIGNUP_SUCCESS;
}
// 这里就是注册成功时,传到reducer中的action类型
export const signupSuccess = (): SignupSuccessAction => ({
type: SIGNUP_SUCCESS,
});
注册失败
注册失败的流程和注册成功的流程类似,只不过在 action 的定义中需要添加一条错误信息。以及在最后需要导出一个认证的 action 类型,这个类型就是所有注册 actions 的联合类型。
export const SIGNUP_FAIL = 'SIGNUP_FAIL';
export interface SignupFailAction {
type: typeof SIGNUP_FAIL;
message: string;
}
// 导出联合类型以供 reducer 使用,这样导出的 AuthUnionType 是上面实现的三种 action 之一
export type AuthUnionType =
| SignupAction
| SignupSuccessAction
| SignupFailAction;
至此,与 action 相关的内容就全部定义好了,也可以开始对 reducer 的实现了。
注册页面的 Reducer
新建注册页面所需的文件,其结构与 action 相对应,为 store > reducers > auth.reducer.ts
,此时的 Redux 结构为:
|- store
| |- actions
| | |- auth.action.ts
| |- reducers
| | |- auth.reducer.ts
| | |- ... # 其余文件省略
Reducer 代码部分逻辑相对简单一点,主要由两个部分组成:
-
state
最开始的申明代表的是初始化状态,鉴于是使用 TypeScript 去实现的,也自然会用 interface 去对状态进行规范化
-
reducer
这里通过 发送(dispatch) 的 action 来判断应该进行什么样的逻辑处理
接下来就根据业务逻辑去实现 authReducer:
// 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: '',
},
};
export default function authReducer(
state = initialState,
action: AuthUnionType
) {
switch (action.type) {
case SIGNUP:
return {
...state,
signup: {
loaded: false,
success: false,
message: '',
},
};
case SIGNUP_SUCCESS:
return {
...state,
signup: {
loaded: true,
success: true,
message: '',
},
};
case SIGNUP_FAIL:
return {
...state,
signup: {
loaded: true,
success: false,
message: action.message,
},
};
// 如果是默认则返回初始状态
default:
return state;
}
}
总体来说,目前注册的 reducer 逻辑还是很简单的,主要的处理逻辑就是:
-
注册刚刚触发时,触发接口、注册成功都设置为 false,报错信息则是空的字符串;
-
注册成功时注册成功都设置为 true,报错信息依旧是空的字符串;
-
注册失败时则接口调用设置为 true,成功设置为 false,并且将 action 中传递来的信息传给 message。
回顾失败的 action 定义是这样的:
export interface SignupFailAction { type: typeof SIGNUP_FAIL; message: string; }
除了 type 之外,action 还直接会传给 reducer 一个 message,表示报错信息。
将注册页面的 reducer 添加到 rooterReducer
这里只需要更新一下 rootReducer 即可,也就是 store > reducers > index.ts
这个文件,内容如下:
const createRootReducer = (history: History) =>
combineReducers({
router: connectRouter(history),
auth: authReducer,
});
注册页面的 Redux Saga
Redux Saga 是负责异步调用的部分,只有 Redux Saga 实现了之后才能够真正的确认程序是否能够正常运行。
Redux-Saga 的首页其实已经将 Redux Saga 的应用步骤写得非常明白了:
-
发送一个 action
这个会在登录的组件内被触发
-
初始化一个副作用
这里指的就是 API 调用
-
将 Redux Saga 挂载到中间件上
现在就按照 1 -> 2 -> 3
的步骤去实现 Redux Saga。
修改注册页面发送 Action
这一步其实就是触发 Redux 的 Action,配置成功的 Saga 可以监听到 Redux 的状态变化,再根据相应的函数去触发副作用,实现的代码如下:
// ...
import { useDispatch } from 'react-redux';
import { signup, SignupPayload } from '../../store/actions/auth.actions';
const Signup = () => {
const dispatch = useDispatch();
const onFinish = (value: SignupPayload) => {
console.log(value);
dispatch(signup(value));
};
return (
<Layout title="注册" subTitle={''}>
<Form onFinish={onFinish}>{/* 省略其他 */}</Form>
</Layout>
);
};
export default Signup;
onFinish
是提交表单时触发的回调函数,是最合适发送 action 的地方。
配置 Saga 并使用 Axios 发送异步请求
这一步需要在 src/store
下面创建一个新的文件夹,并且集中对需要触发 Saga 的状态变化进行处理。
其目录结构如下:
|- src
| |- ...
| |- stroe
| | |- sagas
| | | |- auth.saga.ts # 这里实现验证功能
| | | |- index.ts # Saga 的集中管理
Saga 的功能需要使用 generators,这也是可以算是 generators 处理异步函数的案例了。
-
auth.saga.ts 的实现
import axios from 'axios'; import { takeEvery, put } from 'redux-saga/effects'; import { API } from '../../config'; import { SIGNUP, SignupAction, signupFail, signupSuccess, } from '../actions/auth.actions'; function* handleSignup(action: SignupAction) { try { yield axios.post(`${API}/signup`, action.payload); yield put(signupSuccess()); } catch (error: any) { yield put(signupFail(error.response.data.error)); } } export default function* authSaga() { yield takeEvery(SIGNUP, handleSignup); }
可以看到,
auth.saga
导出的是一个 generator,而auth.saga
中执行异步的函数也是一个 generator。Generator 的优势有:
- 可迭代
- 可中断
- 可继续
- 可暂停
- 可用于循环调用并等待异步操作
- 中断与继续的操作使用
yield
实现
了解了 Generator 的作用,那么代码的作用也比较直接明了了:
每一次
SIGNUP
action 被触发之后,都会调用handleSignup
Generator 进行注册的处理。handleSignup
之中会进行异步函数的调用。根据返回的结果继续触发下一个的 Action。 -
index.ts
import { all } from 'redux-saga/effects'; import authSaga from './auth.saga'; export default function* rootSaga() { yield all([authSaga()]); }
这一段代码的作用类似于
rootReducer
,将所有的 Saga 内容进行其中管理。
挂载 Saga
这部分会将 Saga 挂载到 Redux 中的 Middleware 上,完成最后的配置,操作的文件为 src/store/index.ts
,内容如下:
// ...
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas';
// ...
// create the saga middleware
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
createRootReaducer(history),
applyMiddleware(routerMiddleware(history), logger, sagaMiddleware)
);
// execute the saga middleware
sagaMiddleware.run(rootSaga);
这时候再去提交表单,就会调用 Axios 执行异步操作,并且处理相应的副作用:
为了控制大小截图不是很成功,但是基本上还是显示了一旦注册之后,/signup
的 API 就被调用了。
至此,注册功能就实现了。下一步会优化注册功能:
- 显示注册成功信息
- 显示注册失败信息
- 重置状态
以及实现登陆功能。
以上是关于[React 实战系列] 注册功能的实现的主要内容,如果未能解决你的问题,请参考以下文章
[React 实战系列] 布局登录注册的页面实现及 Route 的封装