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>()
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
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) => ({}),
})
我的 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[]
}
问题:
const employee = useAppSelector(state => selectEmployeeById(state, employeeId));
员工类型如下:
{
id: EntityId;
}
但应该是:
type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
有人可以帮忙吗? :)我希望所选员工的类型为“Employee”,但它是“{ id: EntityId }”
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);