Redux实现token持久化存储

48 阅读2分钟

背景

通常我们做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)

image.png

保存成功!