RTK Query与Redux结合处理Mutation场景的最佳实践及持久化方案
问题解答
1. 结合RTK Query与Redux管理Token的最佳实践
步骤1:创建独立的Token管理Slice
单独创建一个Redux Slice专门管理用户令牌,初始值从localStorage读取,状态更新时自动同步到localStorage:
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; // 从localStorage加载初始token const initialState = { userToken: localStorage.getItem('userToken') || null, }; const userSlice = createSlice({ name: 'user', initialState, reducers: { setUserToken: (state, action: PayloadAction<string | null>) => { state.userToken = action.payload; // 同步更新localStorage if (action.payload) { localStorage.setItem('userToken', action.payload); } else { localStorage.removeItem('userToken'); } }, clearUserToken: (state) => { state.userToken = null; localStorage.removeItem('userToken'); }, }, }); export const { setUserToken, clearUserToken } = userSlice.actions; export default userSlice.reducer;
步骤2:在RTK Query的Mutation中更新Redux状态
修改你的authSlice,利用RTK Query的onQueryStarted钩子,在登录请求成功后触发setUserToken action,同时让Redux自动同步token到localStorage:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setUserToken } from './userSlice'; import type { RootState } from '../store'; // 导入你的RootState类型 export const authSlice = createApi({ reducerPath: "auth", baseQuery: fetchBaseQuery({ baseUrl: resolveApiBaseUrl(), prepareHeaders(headers, { getState }) { headers.set("Content-Type", "application/json"); // 从Redux状态获取token,替代直接读取localStorage const token = (getState() as RootState).user.userToken; if (token) { headers.set("X-XSRF-TOKEN", token); } return headers; } }), endpoints(builder) { return { login: builder.mutation<GymAppResponse, Login>({ query: (payload) => ({ url: "/login", method: "POST", body: payload }), // 请求成功后更新Redux状态 async onQueryStarted(args, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; // 假设接口返回的data包含token字段,根据实际结构调整 dispatch(setUserToken(data.token)); } catch (err) { console.error('登录失败:', err); } }, }) } } }); export const { useLoginMutation } = authSlice;
步骤3:组件中使用Redux状态
组件直接通过useSelector获取token,依赖token的useEffect会自动响应状态变化:
import { useSelector } from 'react-redux'; import type { RootState } from '../store'; function ProtectedComponent() { const userToken = useSelector((state: RootState) => state.user.userToken); useEffect(() => { // token变化时自动执行逻辑,比如页面跳转、数据初始化等 if (userToken) { // 已登录逻辑 } else { // 未登录逻辑 } }, [userToken]); // ...组件其他逻辑 }
2. 替代redux-persist的持久化方案(编程式处理localStorage)
无需额外依赖包,以下是手动处理的最佳实践:
方案1:Slice内部直接同步持久化
如上面的userSlice示例,在reducer的每个状态修改action中,直接同步更新localStorage。这种方式简单直接,适合单个或少量需要持久化的状态。
方案2:封装通用持久化工具函数
如果有多个状态需要持久化,可以封装通用的加载和保存函数:
// utils/persist.ts export const loadState = <T>(key: string): T | undefined => { try { const serializedState = localStorage.getItem(key); if (serializedState === null) return undefined; return JSON.parse(serializedState) as T; } catch (err) { console.error('加载持久化状态失败:', err); return undefined; } }; export const saveState = <T>(key: string, state: T): void => { try { const serializedState = JSON.stringify(state); localStorage.setItem(key, serializedState); } catch (err) { console.error('保存持久化状态失败:', err); } };
在Slice中使用:
import { loadState, saveState } from '../utils/persist'; const initialState = { userToken: loadState<string | null>('userToken') || null, // 其他需要持久化的字段 }; const userSlice = createSlice({ name: 'user', initialState, reducers: { setUserToken: (state, action: PayloadAction<string | null>) => { state.userToken = action.payload; saveState('userToken', action.payload); }, }, });
额外优化:跨标签页状态同步
如果需要处理跨标签页的token同步,可以监听storage事件,在其他标签页修改localStorage时自动更新Redux状态:
// 在App.tsx或store初始化处添加监听 import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { setUserToken } from './userSlice'; function App() { const dispatch = useDispatch(); useEffect(() => { const handleStorageChange = (e: StorageEvent) => { if (e.key === 'userToken') { dispatch(setUserToken(e.newValue)); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, [dispatch]); // ...其他组件逻辑 }
内容的提问来源于stack exchange,提问作者VeRJiL
相关产品推荐
相关产品推荐

