将 RTK 查询与实体适配器和 TypeScript 结合使用

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

这是我的 store.ts 的代码:

import { configureStore } from "@reduxjs/toolkit";
import { apiSlice } from "./api/apiSlice";
import { useDispatch, useSelector } from "react-redux";

export const store = configureStore({
 reducer: {
  [apiSlice.reducerPath]: apiSlice.reducer,
 },

 middleware: (getDefaultMiddleware) => {
  return getDefaultMiddleware().concat(apiSlice.middleware);
 },

 devTools: true,
});

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()

我的数据来自服务器(来自mongodb),类型如下:

type ExtendedEmployee = {
 _id: string    // id assigned by mongodb
 __v: number    // also a mongodb thing
 name: string
 id: string     // id assigned on the frontend before sending it to the server
 supervisorId: string
 subordinates: string[]
}

// id, supervisorId, and subordinates have the same types

这是我的 apiSlice.ts 的代码:

import { createApi, fetchBaseQuery, BaseQueryFn } from "@reduxjs/toolkit/query/react";

export const apiSlice = createApi({
 reducerPath: "api", // optional
 baseQuery: fetchBaseQuery({ baseUrl: "https://mavenup-backend.vercel.app/" }),
 tagTypes: ["Employee"],
 endpoints: (builder) => ({}),
})
我的

employeesApiSlice.ts 的代码:

import { createSelector, createEntityAdapter, EntityState, EntityId } from "@reduxjs/toolkit"; import { apiSlice } from "@/app/api/apiSlice"; import { RootState } from "./store"; type ExtendedEmployee = Employee & { _id: string, __v: number } // the following code is just to visualize the type in its expanded form (nothing to // do with the logic of this application) type ExpandedType = ExpandRecursively< EntityState<Employee, EntityId> >; // no errors if we pass this to builder.query generic arguments // EntityState<{ id: EntityId; }, EntityId> // typeof "employeesAdapter.setAll(initialState, responseData)" = EntityState<{ // id: EntityId; // }, EntityId> const employeesAdapter = createEntityAdapter(); const initialState = employeesAdapter.getInitialState(); // SLICE export const extendedApiSlice = apiSlice.injectEndpoints({ endpoints: (builder) => ({ getEmployees: builder.query<EntityState<Employee, EntityId>, void>({ query: () => "/employees", transformResponse: (responseData: ExtendedEmployee[]): EntityState<Employee, EntityId> => { if (!responseData) return; responseData.map(employee => { delete employee._id; delete employee.__v; }); return employeesAdapter.setAll(initialState, responseData); }, providesTags: () => { return [{ type: "Employee", id: "LIST" }]; }, }), addNewEmployee: builder.mutation({ query: (data) => ({ url: "/employees", method: "POST", body: { data, }, }), invalidatesTags: [{ type: "Employee", id: "LIST" }], }), updateEmployee: builder.mutation({ query: (data) => ({ url: `/employees`, method: "PUT", body: { ...data, }, }), invalidatesTags: () => [{ type: "Employee", id: "LIST" }], }), }), }); // HOOKS export const { useGetEmployeesQuery, useAddNewEmployeeMutation, useUpdateEmployeeMutation, useDeleteEmployeeMutation, } = extendedApiSlice; // SELECTORS (for convenience) // returns the query result object (from cache) export const selectEmployeesResult = extendedApiSlice.endpoints.getEmployees.select(null); // Creates memoized selector const selectEmployeesData = createSelector( selectEmployeesResult, (employeesResult) => employeesResult.data, // normalized state object with ids & entities ); export const { selectAll: selectAllEmployees, selectById: selectEmployeeById, selectIds: selectEmployeeIds, // Pass in a selector that returns the employees slice of state } = employeesAdapter.getSelectors<RootState>((state) => selectEmployeesData(state) ?? initialState);
在上面的代码中,transformResponse 方法从 ExtendedEmployee 类型数据中删除了两个额外的属性(_id 和 __v),并以以下类型“Employee[]”返回其余属性(返回一个数组):

type Employee = { name: string id: string supervisorId: string subordinates: string[] }
问题:

当我在某些组件中选择“getEmployees”端点的结果时,例如:

const employee = useAppSelector(state => selectEmployeeById(state, employeeId));
员工类型如下:

{ id: EntityId; }
但应该是:

type Employee = { name: string id: string supervisorId: string subordinates: string[] }
有人可以帮忙吗? :)

我希望所选员工的类型为“Employee”,但它是“{ id: EntityId }”

redux react-redux redux-toolkit react-typescript rtk-query
1个回答
0
投票
import { createSelector, createEntityAdapter, EntityState, EntityId } from "@reduxjs/toolkit"; import { apiSlice } from "@/app/api/apiSlice"; import { RootState } from "./store"; type ExtendedEmployee = Employee & { _id: string, __v: number }; // type ExpandedType = ExpandRecursively< // EntityState<Employee, EntityId> // >; // { ids: EntityId[], entities: Record<EntityId, Employee>} // no errors / no intellisense // EntityState<{ id: EntityId; }, EntityId> // typeof "employeesAdapter.setAll(initialState, responseData)" = EntityState<{ // id: EntityId; // }, EntityId> const employeesAdapter = createEntityAdapter<Employee>(); const initialState = employeesAdapter.getInitialState(); // SLICE export const extendedApiSlice = apiSlice.injectEndpoints({ endpoints: (builder) => ({ getEmployees: builder.query<EntityState<Employee, string>, void>({ query: () => "/employees", transformResponse: (responseData: ExtendedEmployee[]): EntityState<Employee, string> => { if (!responseData) return; responseData.map(employee => { delete employee._id; delete employee.__v; }); return employeesAdapter.setAll(initialState, responseData); }, providesTags: () => { return [{ type: "Employee", id: "LIST" }]; }, }), addNewEmployee: builder.mutation({ query: (data) => ({ url: "/employees", method: "POST", body: { data, }, }), invalidatesTags: [{ type: "Employee", id: "LIST" }], }), updateEmployee: builder.mutation({ query: (data) => ({ url: `/employees`, method: "PUT", body: { ...data, }, }), invalidatesTags: () => [{ type: "Employee", id: "LIST" }], }), deleteEmployee: builder.mutation({ query: (id) => ({ url: `/employees`, method: "DELETE", body: { ...id, }, }), invalidatesTags: () => [{ type: "Employee", id: "LIST" }], }), }), }); // HOOKS export const { useGetEmployeesQuery, useAddNewEmployeeMutation, useUpdateEmployeeMutation, useDeleteEmployeeMutation, } = extendedApiSlice; // SELECTORS (for convenience) // returns the query result object (from cache) export const selectEmployeesResult = extendedApiSlice.endpoints.getEmployees.select(null); // Creates memoized selector const selectEmployeesData = createSelector( selectEmployeesResult, (employeesResult) => employeesResult.data, // normalized state object with ids & entities ); export const { selectAll: selectAllEmployees, selectById: selectEmployeeById, selectIds: selectEmployeeIds, // Pass in a selector that returns the employees slice of state } = employeesAdapter.getSelectors<RootState>((state) => selectEmployeesData(state) ?? initialState);
    
© www.soinside.com 2019 - 2024. All rights reserved.