如何使用 RTK 查询 createEntityAdapter 来标准化具有“元”和“链接”条目的响应

问题描述 投票:0回答:1

所以,我有一个得到分页用户的响应,它看起来像这样......

{
  data:[{...}, {...}],
  links: {first:..., last:...},
  meta: {current_page: 1, total: 400, from: 1, to: 10}
}

现在,当使用 createEntityAdapter 规范化数据时,我必须仅将对象的“数据”数组传递给它,而不是整个响应

responseData.data
,否则它将无法工作...

    const usersAdapter: any = createEntityAdapter({});
    const initialState = usersAdapter.getInitialState();
    
    export const usersApiSlice = apiSlice.injectEndpoints({
      endpoints: builder => ({
        getUsers: builder.query<MetadataObj, MetadataObj>({
          query: options => ({
            url: "/users",
            params: options,
          }),
          transformResponse: (responseData: MetadataObj) => {
            return usersAdapter.setAll(initialState, responseData.data);
          },
          providesTags: (result, error, arg) => {
            if (result?.ids) {
              return [
                { type: "User", id: "LIST" },
                ...result.ids.map((id: Number) => ({ type: "User", id })),
              ];
            } else return [{ type: "User", id: "LIST" }];
          },
        }),
      }),
    });
    
    export const { useGetUsersQuery } = usersApiSlice;
    
    export const selectUsersResult = usersApiSlice.endpoints.getUsers.select({ page: 1, per_page: 10 });
    const selectUsersData = createSelector(selectUsersResult, usersResult => usersResult.data);
    
    export const {
      selectAll: selectAllUsers,
      selectById: selectUserById,
      selectIds: selectUserIds,
    } = usersAdapter.getSelectors((state: RootState) => selectUsersData(state) ?? initialState);

现在,我如何获得“元”和“链接”键? 我尝试搜索文档,但他们总是假设响应是对象数组。

我现在所做的是在“usersApiSlice”旁边创建了一个“usersSlice”,然后像这样将元数据存储在其中......

import { createSlice } from '@reduxjs/toolkit';

import { MetadataObj } from '../../../types/globalTypes';
import { RootState } from '../../app/store';

type stateType = {
  requestMeta: MetadataObj;
};

const initialState: stateType = {
  requestMeta: {},
};

const usersSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    setRequestMeta: (state, action) => {
      state.requestMeta = action.payload;
    },
  },
});

export const { setRequestMeta } = usersSlice.actions;
export const selectRequestMeta = (state: RootState) => state.user.requestMeta;
export default usersSlice.reducer;

然后我在

transformResponse
函数之后使用
query
函数来捕获原始响应中的元并将其存储在
usersSlice

transformResponse: (responseData: MetadataObj, meta, arg) => {
  store.dispatch(setRequestMeta(responseData.meta));
   return usersAdapter.setAll(initialState, responseData.data);
}

但是,我有一种感觉应该有更好的方法来处理这个问题。我不确定,但应该有。

感谢任何帮助。

normalization rtk-query
1个回答
0
投票

这是我如何让它适用于我的用例的代码片段。尝试理解这段代码是如何工作的,我相信你会解决你的问题。请注意,这是使用 TypeScript 完成的。

import type { EntityId, Dictionary } from "@reduxjs/toolkit";
import { createSelector, createEntityAdapter } from "@reduxjs/toolkit";

import type { IInfo, IPaginatedGetPosts, IPostsForUser } from "../../types";
import { postsApi } from "../api/postsApi";

const postsAdapter = createEntityAdapter({
  selectId: post => post.postId,
  sortComparer: (a: IPostsForUser, b: IPostsForUser) => b.createdAt.localeCompare(a.createdAt),
});

const initialState = postsAdapter.getInitialState({
  info: {
    currentPage: 0,
    numberOfPosts: 0,
    pageSize: 0,
    pages: 0,
    next: null,
    previous: null,
  },
});

export const extendedApiSlice = postsApi.injectEndpoints({
  endpoints: builder => ({
    // we are getting a response of type IPaginatedGetPosts but we are transforming that response when normalizing state
    // so this is the new return type of the query
    getPosts: builder.query<
      {
        info: IInfo;
        ids: EntityId[];
        entities: Dictionary<IPostsForUser>;
      },
      void
    >({
      query: () => {
        return {
          // allow httpOnly cookies to be sent
          credentials: "include",
          method: "GET",
          url: "/get-posts",
        };
      },
      transformResponse: (rawResult: IPaginatedGetPosts) => {
        // set initial state to rawResult.postsForUser and also set info from initialState to rawResult.info
        return postsAdapter.setAll({ ...initialState, info: rawResult.info }, rawResult.postsForUser);
      },
      providesTags: result => {
        if (result) {
          // result is object of entities ids and info
          // entities is an object with keys of ids and values of posts
          // we need to turn it into an array of posts
          return [
            ...Object.values(result.entities).map(post => ({ type: "Post" as const, postId: post.postId, userId: post.User.userId })),
            { type: "Post", postId: "LIST", userId: "LIST" },
          ];
        }
        return [{ type: "Post", postId: "LIST", userId: "LIST" }];
      },
    }),
  }),
});

export const { useGetPostsQuery } = extendedApiSlice;
© www.soinside.com 2019 - 2024. All rights reserved.