I'm trying to decode a JWT when a user logs in on my app in order to get what kind of roles and access the user should have for the application. I get the roles fine from the token but it's trying to dispatch them to the Redux store and console log the result elsewhere that just returns undefined:
My decoded roles looks like this:
roles: [
"SUPER_ADMIN",
"CUSTOMER_CARE"
]
I can't figure out where it's going wrong so any help would be greatly appreciated.
This is my auth file that handles login and decoding, as well as the dispatch:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { BASE_API_URI } from '../../constants';
import jwtDecode from 'jwt-decode';
import { setRoles } from '../../store/roles/rolesSlice';
import { store } from '../../main';
import { Role } from '../../constants';
export const TagTypes = {
User: 'User',
};
const rawBaseQuery = fetchBaseQuery({
baseUrl: `${BASE_API_URI}/api/v1/`,
headers: { Accept: "application/json" },
prepareHeaders: (headers) => {
const token = localStorage.getItem("access_token");
if (token) {
headers.set("Authorization", `Bearer ${token}`);
// Decode token using jwt-decode and extract roles
const decodedToken: any = jwtDecode(token);
const roles: Role[] = decodedToken.roles;
console.log('roles ', roles);
store.dispatch(setRoles(roles));
}
return headers;
},
mode: "cors",
});
const baseQuery = async (args: any, api: any, extraOptions: any): Promise<any> => {
let result = await rawBaseQuery(args, api, extraOptions);
if (result.error && result.error.status === 401) {
window.localStorage.removeItem("access_token");
window.location.href = "/login";
}
return result;
};
export const apiService = createApi({
reducerPath: "api",
baseQuery: baseQuery,
tagTypes: [
TagTypes.User,
],
endpoints: () => ({
// SEE SUBFOLDERS
}),
});
And my main file with my store:
import React from 'react'
import './main.scss'
import { I18nextProvider } from 'react-i18next'
import i18n from './services/i18n'
import ReactDOM from 'react-dom/client'
import './styles/index.scss'
import "@xbooks/ui/style.css";
import Router from './features/router'
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import { apiService } from "./services/api";
import { setDefaultOptions } from "date-fns";
import { it } from 'date-fns/locale'
import rootReducer from './store/rootReducer';
setDefaultOptions({ locale: it })
export const store = configureStore({
reducer: {
// Add reducers here
...rootReducer,
[apiService.reducerPath]: apiService.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiService.middleware),
});
ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
.render(
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<Router />
</I18nextProvider>
</Provider>
)
This is my rootReducer file:
import { combineReducers } from '@reduxjs/toolkit';
import rolesReducer from './roles/rolesSlice';
const rootReducer = combineReducers({
roles: rolesReducer,
// other reducers can be added here
});
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
And my rolesReducer:
import { createSlice } from '@reduxjs/toolkit';
const rolesSlice = createSlice({
name: 'roles',
initialState: [] as string[],
reducers: {
setRoles: (state, action) => {
return action.payload;
},
},
});
export const { setRoles } = rolesSlice.actions;
export default rolesSlice.reducer;
And finally, my selector:
import {RootState} from '../rootReducer';
export const selectRoles = (state: RootState) => state.roles;
I now import selectRoles from my selector, and useSelector from react-redux and call:
const roles = useSelector(selectRoles);
console.log('roles selector ', roles);
This is part where I expected the roles to be logged but just get undefined.
Turns out the issue was my store trying to have multiple reducers in the object, I'm not sure why but removing the apiService reducer and placing it into rootReducer fixed everything.
This is now my store:
And I added the apiService.reducerPath to my rootReducer here: