Redux Toolkit Query实现Auth持久化与Reducer调用方法
Redux Toolkit 鉴权状态实现+持久化完整方案
以下是可直接落地的全流程实现,直接替换对应文件代码即可跑通:
1. 修正鉴权切片代码
你当前存储的是单用户登录态,不是列表类数据,不需要引入createEntityAdapter,精简后的authSlice代码如下:
// src/features/auth/authSlice.js import { createSlice } from "@reduxjs/toolkit"; const initialState = { user: null, token: null, is_logged_in: false } export const authSlice = createSlice({ name: 'auth', initialState, reducers: { // 退出登录直接重置为初始状态 logout: () => initialState, // 登录成功/本地缓存恢复时写入登录态 setCredentials: (state, action) => { state.user = action.payload.user state.token = action.payload.token state.is_logged_in = true } } }) export const { logout, setCredentials } = authSlice.actions export default authSlice.reducer // 预定义常用选择器,组件中可直接调用取值 export const selectCurrentUser = state => state.auth.user export const selectCurrentToken = state => state.auth.token export const selectIsLoggedIn = state => state.auth.is_logged_in
2. 改造RTK Query基础配置
需要给公共请求逻辑加自动带token、token过期自动登出逻辑,同时把登录、登出接口注册到api切片中,接口请求成功后自动触发状态更新:
// src/app/api/apiSlice.js import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; import { setCredentials, logout } from "../../features/auth/authSlice"; // 基础请求配置 const baseQuery = fetchBaseQuery({ baseUrl: "https://ab91-203-82-55-110.ngrok.io/", // 请求拦截:自动从store取token加到请求头 prepareHeaders: (headers, { getState }) => { const token = getState().auth.token if (token) { headers.set('authorization', `Bearer ${token}`) } return headers } }) // 包装基础请求,处理401未授权场景自动登出 const baseQueryWithReauth = async (args, api, extraOptions) => { let result = await baseQuery(args, api, extraOptions) if (result?.error?.status === 401) { api.dispatch(logout()) } return result } export const apiSlice = createApi({ reducerPath: "api", baseQuery: baseQueryWithReauth, tagTypes: ['Post', 'User'], endpoints: builder => ({ // 登录接口 login: builder.mutation({ query: (credentials) => ({ url: '/login', method: 'POST', body: credentials }), // 接口请求成功后自动把用户信息、token存入auth切片 onQueryStarted: async (arg, { dispatch, queryFulfilled }) => { try { const { data } = await queryFulfilled dispatch(setCredentials({ user: data.data, token: data.access_token })) } catch (err) { // 登录失败无需额外处理,组件内可捕获错误做提示 } } }), // 登出接口(后端需要销毁token时启用) logout: builder.mutation({ query: () => ({ url: '/logout', method: 'POST' }), onQueryStarted: async (arg, { dispatch, queryFulfilled }) => { await queryFulfilled dispatch(logout()) } }) }) }) export const { useLoginMutation, useLogoutMutation } = apiSlice
你之前写的postSlice、userSlice的接口注入逻辑不需要改动,直接基于这个新的apiSlice注入即可。
3. 配置Store与状态持久化
持久化逻辑只需要缓存auth切片内容即可,RTK Query的接口缓存不需要持久化:
- 先安装依赖:执行命令
npm install redux-persist - 编写store配置:
// src/app/store.js import { configureStore } from '@reduxjs/toolkit' import { apiSlice } from './api/apiSlice' import authReducer from '../features/auth/authSlice' // 引入持久化相关依赖 import storage from 'redux-persist/lib/storage' import { persistReducer, persistStore, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist' // 持久化配置:只缓存auth切片 const persistConfig = { key: 'root', storage, whitelist: ['auth'] } const persistedAuthReducer = persistReducer(persistConfig, authReducer) export const store = configureStore({ reducer: { [apiSlice.reducerPath]: apiSlice.reducer, auth: persistedAuthReducer, // 其余业务切片(post、user等)按原有方式注册即可 }, middleware: getDefaultMiddleware => getDefaultMiddleware({ // 忽略持久化相关action的序列化检查,避免控制台报错 serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER] } }).concat(apiSlice.middleware) }) export const persistor = persistStore(store)
- 在项目入口文件包裹持久化网关,保证页面加载前先完成本地缓存状态的恢复:
// src/main.jsx import React from 'react' import ReactDOM from 'react-dom/client' import { Provider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { store, persistor } from './app/store' import App from './App' ReactDOM.createRoot(document.getElementById('root')).render( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </Provider> )
4. 组件内调用示例
以登录组件为例,不需要手动dispatch setCredentials,接口调用成功后会自动更新状态、写入本地缓存:
import { useLoginMutation } from '../../app/api/apiSlice' import { useSelector } from 'react-redux' import { selectCurrentUser, selectIsLoggedIn } from './authSlice' import { useNavigate } from 'react-router-dom' const Login = () => { const [login, { isLoading }] = useLoginMutation() const navigate = useNavigate() const isLoggedIn = useSelector(selectIsLoggedIn) const currentUser = useSelector(selectCurrentUser) const handleSubmit = async (e) => { e.preventDefault() const formData = new FormData(e.target) try { await login({ username: formData.get('username'), password: formData.get('password') }).unwrap() navigate('/') } catch (err) { console.error('登录失败', err) } } if (isLoggedIn) return <div>当前登录用户:{currentUser.name}</div> return ( <form onSubmit={handleSubmit}> <input name="username" placeholder="用户名" required /> <input name="password" type="password" placeholder="密码" required /> <button disabled={isLoading} type="submit"> {isLoading ? '登录中...' : '登录'} </button> </form> ) } export default Login
登出时直接调用useLogoutMutation即可,会自动清空store状态和本地缓存。
该方案不需要单独实现LOCAL_LOGIN逻辑,页面刷新时持久化层会自动从本地存储读取auth状态完成初始化,所有接口请求会自动携带token,token失效时会自动重置登录态。
内容的提问来源于stack exchange,提问作者Engr.Aftab Ufaq
相关产品推荐
相关产品推荐

