如何用RTK Query的createEntityAdapter保留含meta/links的响应数据
优化RTK Query分页数据与元数据的存储方案
你当前通过额外创建slice并手动dispatch存储元数据的方式可行,但确实有更贴合RTK Query设计的优化方案——直接扩展createEntityAdapter的初始状态,将meta和links与归一化的实体数据一同存入RTK Query的缓存,无需额外维护独立slice。
核心思路
createEntityAdapter的getInitialState()支持传入额外字段扩展状态结构,我们可以把分页所需的meta和links直接附加到归一化后的实体状态中,在transformResponse里一次性返回完整状态,让RTK Query自动缓存这些数据,同时保留entityAdapter的所有选择器能力。
具体实现代码
1. 定义扩展后的状态类型与Adapter
import { createEntityAdapter, EntityState } from '@reduxjs/toolkit'; import { apiSlice } from './apiSlice'; import { RootState } from '../../app/store'; // 假设你的用户实体类型为User interface User { id: number; // 其他用户字段 } // 扩展EntityState,加入分页元数据字段 interface UsersState extends EntityState<User> { meta?: { current_page: number; total: number; from: number; to: number; }; links?: { first?: string; last?: string; // 其他链接字段 }; } // 创建Adapter并初始化带扩展字段的状态 const usersAdapter = createEntityAdapter<User>(); const initialState = usersAdapter.getInitialState<UsersState>({ meta: undefined, links: undefined });
2. 修改RTK Query端点配置
export const usersApiSlice = apiSlice.injectEndpoints({ endpoints: builder => ({ getUsers: builder.query<UsersState, { page: number; per_page: number }>({ query: options => ({ url: "/users", params: options, }), transformResponse: (responseData: { data: User[], meta: UsersState['meta'], links: UsersState['links'] }) => { // 先归一化用户数据,再合并meta和links const normalizedData = usersAdapter.setAll(initialState, responseData.data); return { ...normalizedData, meta: responseData.meta, links: responseData.links }; }, providesTags: (result, error, arg) => { if (result?.ids) { return [ { type: "User", id: "LIST" }, ...result.ids.map(id => ({ type: "User", id })), ]; } else return [{ type: "User", id: "LIST" }]; }, }), }), }); export const { useGetUsersQuery } = usersApiSlice;
3. 定义选择器获取数据与元数据
export const selectUsersResult = usersApiSlice.endpoints.getUsers.select({ page: 1, per_page: 10 }); // 获取完整的用户状态(包含实体、meta、links) const selectUsersState = createSelector(selectUsersResult, usersResult => usersResult.data ?? initialState); // 复用entityAdapter的选择器获取用户实体 export const { selectAll: selectAllUsers, selectById: selectUserById, selectIds: selectUserIds, } = usersAdapter.getSelectors((state: RootState) => selectUsersState(state)); // 自定义选择器获取分页元数据 export const selectUsersMeta = createSelector(selectUsersState, state => state.meta); export const selectUsersLinks = createSelector(selectUsersState, state => state.links);
方案优势
- 无需额外slice:元数据与实体数据共存于RTK Query的缓存中,避免手动dispatch的冗余操作
- 分页缓存隔离:不同分页参数(如
page=1和page=2)的元数据会被分别缓存,不会互相覆盖 - 类型安全:通过TypeScript扩展状态类型,确保元数据的类型正确性
- 统一管理:所有与用户列表相关的数据(实体+分页信息)集中在一处,便于维护
内容的提问来源于stack exchange,提问作者Ruby
相关产品推荐
相关产品推荐

