在本项目中,我们使用了 Redux Toolkit (以下简称 RTK)来进行应用状态管理。相比于传统的 Redux 配置方式,RTK 提供了更简洁的 API 和更合理的默认配置,例如内置 immer、redux-thunk、devTools 等,让我们可以专注于业务逻辑而非繁琐的样板代码。
一、Redux Toolkit 的优点
-
简化配置
- 不再需要手动安装和配置
redux-thunk、redux-devtools-extension等常用中间件,RTK 内部已经默认集成。 - 不必手动编写冗长的
combineReducers、createStore等函数,大大减少样板代码。
- 不再需要手动安装和配置
-
内置可变数据的支持
- RTK 默认使用 immer 来自动管理不可变数据,直接在 reducer 中对
state进行“可变”操作,实际上底层会帮你确保数据不可变。
- RTK 默认使用 immer 来自动管理不可变数据,直接在 reducer 中对
-
统一的逻辑组织
- RTK 官方推荐使用 Slices 来按逻辑模块或业务领域拆分状态。每个 slice 包含
reducer、action、selector等,结构清晰。 - 大大减少了样板式的
actionTypes.js、actions.js、reducers.js等文件,利于维护和拓展。
- RTK 官方推荐使用 Slices 来按逻辑模块或业务领域拆分状态。每个 slice 包含
-
更好的开发者体验
- 内置的
createAsyncThunk、createSlice等方法让异步逻辑处理、action 和 reducer 的创建更加流畅。 - 支持与 TypeScript 深度结合,可自动推导函数、状态及 dispatch 的类型。
- 内置的
二、项目中的 Redux 结构
1. 根 store 与切片
在项目中,我们通过以下方式来配置 store,并将多个 slice(如 authSlice, themeSlice 等)合并到一起:
// src/store/index.ts
import { combineSlices, configureStore } from '@reduxjs/toolkit';
import type { Action, ThunkAction } from '@reduxjs/toolkit';
import { authSlice } from '../features/auth/authStore';
import { routeSlice } from '../features/router/routeStore';
import { tabSlice } from '../features/tab/tabStore';
import { themeSlice } from '../features/theme';
import { appSlice } from '../layouts/appStore';
// 使用 combineSlices 自动组合所有 slice
const rootReducer = combineSlices(appSlice, authSlice, themeSlice, routeSlice, tabSlice);
export type RootState = ReturnType<typeof rootReducer>; // 推导出根 state 类型
export const store = configureStore({
reducer: rootReducer
});
export type AppStore = typeof store;
export type AppDispatch = AppStore['dispatch'];
// thunk action 类型
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action>;combineSlices: 自定义辅助函数,用于简化combineReducers的操作;它能够自动识别每个 slice 的reducerPath并进行合并。RootState: 通过ReturnType<typeof rootReducer>自动推导整个应用的根状态类型,给useSelector等带来类型推导。AppDispatch: 通过store.dispatch类型推导得到,给useDispatch等带来类型提示。
2. 定义与导出自定义 Hooks
项目中通过以下 hook 访问 Redux store,更好地与 TypeScript 结合:
// src/hooks/useStore.ts (或其他命名)
import { useDispatch, useSelector } from 'react-redux';
import type { AppDispatch, RootState } from '@/store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();useAppDispatch:封装了原生的useDispatch,并带上AppDispatch类型。useAppSelector:封装了原生的useSelector,并带上RootState类型。
使用示例:
const dispatch = useAppDispatch();
const someValue = useAppSelector(state => state.someSlice.someValue);3. Slice 示例
// src/features/router/routeStore.ts
import { createAppSlice } from '../../store/createAppSlice';
import type { PayloadAction } from '@reduxjs/toolkit';
interface InitialStateType {
cacheRoutes: string[];
removeCacheKey: string | null;
routeHomePath: string;
}
const initialState: InitialStateType = {
cacheRoutes: [],
removeCacheKey: null,
routeHomePath: import.meta.env.VITE_ROUTE_HOME
};
export const routeSlice = createAppSlice({
initialState,
name: 'route',
reducers: create => ({
addCacheRoutes: create.reducer((state, { payload }: PayloadAction<string>) => {
state.cacheRoutes.push(payload);
}),
resetRouteStore: create.reducer(() => initialState),
setCacheRoutes: create.reducer((state, { payload }: PayloadAction<string[]>) => {
state.cacheRoutes = payload;
}),
setHomePath: create.reducer((state, { payload }: PayloadAction<string>) => {
state.routeHomePath = payload;
}),
setRemoveCacheKey: create.reducer((state, { payload }: PayloadAction<string | null>) => {
state.removeCacheKey = payload;
})
}),
// 这里也可以直接定义选择器,统一导出
selectors: {
selectCacheRoutes: route => route.cacheRoutes,
selectRemoveCacheKey: route => route.removeCacheKey,
selectRouteHomePath: route => route.routeHomePath
}
});
// actions
export const {
addCacheRoutes,
resetRouteStore,
setCacheRoutes,
setHomePath,
setRemoveCacheKey
} = routeSlice.actions;
// selectors
export const {
selectCacheRoutes,
selectRemoveCacheKey,
selectRouteHomePath
} = routeSlice.selectors;createAppSlice:一个对createSlice的封装,简化了 reducers、action、selector 的定义,减少重复代码。- 采用
selectors字段统一定义选择器(Selectors),再在最后一起导出,使用useAppSelector时可以直接调用。
三、如何访问和修改 Redux 状态
在任意组件中,通过自定义 Hook来读取或更新状态:
import React, { useMemo } from 'react';
import { useAppDispatch, useAppSelector } from '@/hooks/useStore';
import {
selectActiveFirstLevelMenuKey,
setActiveFirstLevelMenuKey
} from '@/features/menuStore'; // 伪示例
function ExampleMenu() {
const dispatch = useAppDispatch();
const activeFirstLevelMenuKey = useAppSelector(selectActiveFirstLevelMenuKey);
// 读取或计算派生数据
const menus = useMemo(() => {
// ...根据路由或其他数据生成菜单
return [];
}, []);
// 调用 actions 来更新 redux state
const handleChangeMenuKey = (key?: string) => {
dispatch(setActiveFirstLevelMenuKey(key || ''));
};
return (
<div>
<p>当前激活菜单: {activeFirstLevelMenuKey}</p>
{/* 渲染菜单 */}
{/* 点击菜单项时调用 handleChangeMenuKey 来更新状态 */}
</div>
);
}
export default ExampleMenu;- 读取:
useAppSelector(selectActiveFirstLevelMenuKey)。 - 更新:
dispatch(setActiveFirstLevelMenuKey(newKey))。
四、总结
- Redux Toolkit 提供了更简洁、功能齐全的方式来管理全局状态,相比传统 Redux 省去了大量配置和模板代码。
- Slices:
- 通过
createSlice/createAppSlice分别管理各领域状态(如auth,theme,route,tab等)。 - 将 reducers、actions、selectors 集中到单一文件,降低维护成本。
- 通过
- 自定义 Hook:
useAppDispatch和useAppSelector搭配 TypeScript,可以获得优秀的类型推断及编译期检查。
- 状态读写示例:
useAppSelector获取全局状态中的所需部分;dispatch(actionCreator())触发 reducer 更新状态。
通过这种模式,项目可保持状态管理清晰可控,同时借助 TypeScript 的能力有效避免常见的动态类型错误,大大提升可维护性和开发效率。希望你在本项目中能顺利使用 Redux Toolkit 的各项特性,写出高质量的状态管理逻辑。
Last updated on