You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的接口缓存不需要持久化:

  1. 先安装依赖:执行命令npm install redux-persist
  2. 编写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)
  1. 在项目入口文件包裹持久化网关,保证页面加载前先完成本地缓存状态的恢复:
// 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:21:32