名称: redux-toolkit 描述: 包含切片创建、异步thunk、RTK Query、状态规范化以及DevTools集成的Redux Toolkit模式。 允许使用的工具: 读取、写入、编辑、Bash、Glob、Grep
Redux Toolkit 技能
为在React应用中实现Redux Toolkit提供专家级协助,涵盖现代模式和最佳实践。
能力
- 创建包含reducer和action的Redux切片
- 使用createAsyncThunk实现异步操作
- 设置用于数据获取的RTK Query
- 使用createEntityAdapter配置状态规范化
- 集成Redux DevTools和中间件
- 使用TypeScript为Redux状态和action添加类型
使用场景
在以下情况下调用此技能:
- 在新项目中设置Redux Toolkit
- 创建具有类型化状态的功能切片
- 实现异步数据获取
- 配置RTK Query端点
- 从传统Redux迁移
输入参数
| 参数 | 类型 | 是否必需 | 描述 |
|---|---|---|---|
| featureName | 字符串 | 是 | 功能/切片的名称 |
| stateShape | 对象 | 是 | 初始状态结构 |
| asyncActions | 数组 | 否 | 要创建的异步thunk |
| useRTKQuery | 布尔值 | 否 | 是否使用RTK Query |
| entityAdapter | 布尔值 | 否 | 使用实体适配器进行规范化 |
配置示例
{
"featureName": "users",
"stateShape": {
"items": [],
"selectedId": null,
"status": "idle",
"error": null
},
"asyncActions": ["fetchUsers", "createUser", "updateUser"],
"useRTKQuery": false,
"entityAdapter": true
}
生成模式
使用实体适配器的切片
import { createSlice, createEntityAdapter, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from '../../app/store';
interface User {
id: string;
name: string;
email: string;
}
const usersAdapter = createEntityAdapter<User>({
selectId: (user) => user.id,
sortComparer: (a, b) => a.name.localeCompare(b.name),
});
interface UsersState extends ReturnType<typeof usersAdapter.getInitialState> {
status: 'idle' | 'loading' | 'succeeded' | 'failed';
error: string | null;
}
const initialState: UsersState = usersAdapter.getInitialState({
status: 'idle',
error: null,
});
const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
userAdded: usersAdapter.addOne,
userUpdated: usersAdapter.updateOne,
userRemoved: usersAdapter.removeOne,
usersReceived: usersAdapter.setAll,
},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.status = 'succeeded';
usersAdapter.setAll(state, action.payload);
})
.addCase(fetchUsers.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message ?? 'Failed to fetch users';
});
},
});
export const { userAdded, userUpdated, userRemoved, usersReceived } = usersSlice.actions;
export const {
selectAll: selectAllUsers,
selectById: selectUserById,
selectIds: selectUserIds,
} = usersAdapter.getSelectors<RootState>((state) => state.users);
export default usersSlice.reducer;
异步Thunk
import { createAsyncThunk } from '@reduxjs/toolkit';
import { usersApi } from '../api/users.api';
export const fetchUsers = createAsyncThunk(
'users/fetchUsers',
async (_, { rejectWithValue }) => {
try {
const response = await usersApi.getAll();
return response.data;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
export const createUser = createAsyncThunk(
'users/createUser',
async (userData: CreateUserDto, { rejectWithValue }) => {
try {
const response = await usersApi.create(userData);
return response.data;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
RTK Query API
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
interface User {
id: string;
name: string;
email: string;
}
export const usersApi = createApi({
reducerPath: 'usersApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['User'],
endpoints: (builder) => ({
getUsers: builder.query<User[], void>({
query: () => 'users',
providesTags: (result) =>
result
? [...result.map(({ id }) => ({ type: 'User' as const, id })), 'User']
: ['User'],
}),
getUserById: builder.query<User, string>({
query: (id) => `users/${id}`,
providesTags: (result, error, id) => [{ type: 'User', id }],
}),
createUser: builder.mutation<User, Partial<User>>({
query: (body) => ({
url: 'users',
method: 'POST',
body,
}),
invalidatesTags: ['User'],
}),
updateUser: builder.mutation<User, Partial<User> & Pick<User, 'id'>>({
query: ({ id, ...patch }) => ({
url: `users/${id}`,
method: 'PATCH',
body: patch,
}),
invalidatesTags: (result, error, { id }) => [{ type: 'User', id }],
}),
}),
});
export const {
useGetUsersQuery,
useGetUserByIdQuery,
useCreateUserMutation,
useUpdateUserMutation,
} = usersApi;
Store配置
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import usersReducer from '../features/users/usersSlice';
import { usersApi } from '../features/users/usersApi';
export const store = configureStore({
reducer: {
users: usersReducer,
[usersApi.reducerPath]: usersApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(usersApi.middleware),
});
setupListeners(store.dispatch);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
最佳实践
- 服务器状态使用RTK Query,客户端状态使用切片
- 使用createEntityAdapter规范化嵌套数据
- 正确地为所有状态和action添加类型
- 使用选择器处理派生状态
- 保持切片专注于单一功能
目标流程
- react应用开发
- 状态管理设置
- MERN栈开发
- 企业级React开发