背景
通常我们做web项目的时候,都会接触到用户登录业务,判断用户的登录状态,我们在vue项目中用到的是pinia,而在React项目中就要用到状态管理工具Redux
安装
npm i redux react-redux redux-persist redux-thunk
redux:是一个独立的应用数据流框架,主要用于解决组件间的状态共享问题,通过集中式管理状态来实现
react-redux:是一个连接Redux和React的库,它提供了一些API和工具,使得在React应用中使用Redux变得更加方便
redux-persist:数据持久化使用
redux-thunk:异步处理
其他库: npm i --save-dev @types/redux-persist
npm i immer //处理嵌套对象
代码实现
//redux/index.ts
import { legacy_createStore as createStore, applyMiddleware, combineReducers, compose, Store } from "redux";
import { persistStore } from "redux-persist";
import persistReducer from "redux-persist/es/persistReducer";
import storage from "redux-persist/lib/storage";
import { thunk } from "redux-thunk";
//redux持久化配置
const persistConfig = {
key: "redux-state",
storage: storage,
}
//创建reducer(通过组合方式)
const reducer = combineReducers({
global,
})
//创建一个新的reducer
const persistReducerConfig = persistReducer(persistConfig, reducer);
//开启redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
//使用redux中间件
const middleWare = applyMiddleware(thunk);
//创建store
const store: Store = createStore(persistReducerConfig, composeEnhancers(middleWare));
//创建持久化store
const persistor = persistStore(store);
export { store, persistor };
//redex/interface/index.ts
export interface GlobalState {
token: string;
}
//redux/mutation-types.ts
//设置token
export const SET_TOKEN = 'SET_TOKEN';
//redux/modules/global/actions.ts
import * as types from '@/redux/mutation-types'
// setToken
export const setToken = (token: string)=>{
return {
type: types.SET_TOKEN,
token
}
}
//redux/modules/global/reducer.ts
import { GlobalState } from '@/redux/interface/index'
import { Action } from 'redux'
import * as types from '@/redux/mutation-types'
import { Draft, produce } from 'immer'
const globalState: GlobalState = {
token: "",
}
interface SetTokenAction extends Action {
type: typeof types.SET_TOKEN,
token: string
}
type ActionType = SetTokenAction;
const global = (state: GlobalState = globalState, action: ActionType) => {
return produce(
state,
(draftState: Draft<GlobalState>) => {
switch (action.type) {
case types.SET_TOKEN:
draftState.token = action.token;
break;
default:
break;
}
}
)
}
export default global;
//登录页
import "./index.less"
import { FormProps } from "antd"
import { Form, Button, Input, message } from "antd"
import { login } from "@/pages/api/api"
import { setToken } from "@/redux/modules/global/action";
import { connect } from "react-redux";
type FieldType = {
username?: string;
password?: string;
}
interface LoginProps {
setToken:typeof setToken;
}
function Login(props: LoginProps) {
//保存登录信息
const handleLogin = (token:string) =>{
const { setToken } = props;
setToken(token);
}
//提交登录
const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
const { username, password } = values
login({ username: username, password: password }).then((res: any) => {
const info = res.data
if (info.code == "2000") {
//登录信息
const userInfo = info.data
handleLogin(userInfo?.id);
} else {
message.error(info.msg)
}
})
}
const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (errorInfo) => {
console.log('Failed!', errorInfo);
}
return (
<div className="login-container">
<div className="login-box">
<Form name="login"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item<FieldType>
label="用户名"
name="username"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input />
</Form.Item>
<Form.Item<FieldType>
label="密码"
name="password"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password />
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit">
登录
</Button>
</Form.Item>
</Form>
</div>
</div>
)
}
//映射dispatch到props
const mapDispatchToProps = { setToken }
export default connect(null, mapDispatchToProps)(Login)
保存成功!