Here is the code for my store.ts:
<code>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>()
</code>
<code>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>()
</code>
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>()
My data comes from the server (from mongodb) with the following type:
<code>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
</code>
<code>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
</code>
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
here is the code for my apiSlice.ts:
<code>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) => ({}),
})
</code>
<code>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) => ({}),
})
</code>
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) => ({}),
})
code for my employeesApiSlice.ts:
<code>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);
</code>
<code>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);
</code>
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);
in the above code, transformResponse method gets rid of two extra properties (_id and __v) from ExtendedEmployee type data and returns the rest in the following type “Employee[]” (returns an array):
<code>type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
</code>
<code>type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
</code>
type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
The Problem:
when I select the result of the “getEmployees” endpoint in some of my components like:
<code>const employee = useAppSelector(state => selectEmployeeById(state, employeeId));
</code>
<code>const employee = useAppSelector(state => selectEmployeeById(state, employeeId));
</code>
const employee = useAppSelector(state => selectEmployeeById(state, employeeId));
the type of employee is the following:
<code>{
id: EntityId;
}
</code>
<code>{
id: EntityId;
}
</code>
{
id: EntityId;
}
but it should be:
<code>type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
</code>
<code>type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
</code>
type Employee = {
name: string
id: string
supervisorId: string
subordinates: string[]
}
Can anyone help please? 🙂
I want the type of selected employee to be “Employee” but it is “{ id: EntityId }”
New contributor
Irfan is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.