0

I have implemented OAuth with MSAL-React (Microsoft authentication to enable SSO) in my application. I want the accessToken acquired from MSAL to be accessible across the pages. So, I thought of using redux to store the accessToken. However, I could retrieve the accessToken from redux store after a few refresh. In order to solve this , I got to know, I have to use Middleware. I have tried using middleware. My I keep on getting this error

WebpackError: Expected the root reducer to be a function. Instead, received: 'undefined'

Please let me know where am I doing wrong

store.js

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import rootReducer from './rootReducer'

const composedEnhancer = composeWithDevTools(applyMiddleware(thunkMiddleware))

// The store now has the ability to accept thunk functions in `dispatch`
const store = createStore(rootReducer, composedEnhancer)
export default store

rootReducer.js

import { fetchUsersToken, reducer } from "./reducer";

import { combineReducers } from "redux";

 //as we have only one reducer , if we have multiple reducer then we can import and add 
 below to current reducer

 //previously I have given like "export const rootReducer = combineReducers({...})"
 export const rootReducer = () => combineReducers({ 
    reduxState: reducer
  });

reducer.js

import { useMsal } from "@azure/msal-react";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { loginRequest } from "./authConfig";
import { msalInstance } from "./pages";
import * as actionTypes from "./action-types";

const initialState = []

export default function reducer(state = initialState, action) {
  switch (action.type) {
// omit other reducer cases
case actionTypes.GETTOKEN: {
  // Replace the existing state entirely by returning the new value
  return action.payload
}
default:
  return state
}
}

export const fetchUsersToken = createAsyncThunk("users/fetchUsersToken", async 
(dispatch, getState) => {
 const {accounts} = useMsal()
 const request = {
 ...loginRequest,
 account: accounts[0],
 }
 const tkn = await msalInstance.acquireTokenSilent(request)
 dispatch({ type: actionTypes.GETTOKEN, payload: tkn.accessToken })
  });

index.js

import React from "react"
import { Helmet } from "react-helmet"
import { PublicClientApplication } from "@azure/msal-browser"
import { MsalProvider } from "@azure/msal-react"
import { msalConfig } from "../authConfig"
import PageLayout from "./PageLayout"
import { createTheme, ThemeProvider } from "@mui/material/styles"

//Redux
 import { Provider } from "react-redux";
 import {store} from "../store";

 store.dispatch(fetchUsersToken());

 //Redux Ends here
 export const msalInstance = new PublicClientApplication(msalConfig)

 //For changing default blue color for mui text-fields
 const theme = createTheme({
  palette: {
   primary: { main: "#000000" },
   },
   })

   export default function Home() {
   return (
    <>
  <Helmet>
    <title>XXXXXXXXXX</title>
  </Helmet>
  <MsalProvider instance={msalInstance}>
    <ThemeProvider theme={theme}>
    <Provider store={store}>
      <PageLayout />
      </Provider>
    </ThemeProvider>
  </MsalProvider>
</>
 )
  }
Lakshmi
  • 85
  • 12

1 Answers1

0

Two things:

  • your rootReducer is a named export, not a default one, so you have to import it with {}:
import { rootReducer } from './rootReducer'
  • your rootReducer should be a call of normal configureStore, not a function returning that:
 export const rootReducer = combineReducers({ 
  • you are writing a very old style of Redux here, and at the place where you call createStore should even get a warning about that. Modern Redux look fundamentally different and is about 1/4 of that code. I would highly recommend you to read why Redux Toolkit is how to use Redux today and then follow the official Redux tutorial to learn Modern Redux instead of the legacy style you use here.
    (Looking again, you kinda are... using both styles in parallel? You really don't need createStore, use configureStore instead. Also, don't hand-write reducers, but stay with createSlice. Also, don't manually dispatch the final result when using createAsyncThunk, that's missing the point of cAT. Really maybe give that tutorial I linked a go.)
phry
  • 35,762
  • 5
  • 67
  • 81
  • Thanks a lot. But I'm not getting the updated accessToken value from the store. I have given initial value as "Hello world". That is only I'm getting – Lakshmi Dec 23 '22 at 09:58
  • Can someone tell me is this correct way of writing in the slice file ? fetchUsersToken = createAsyncThunk({}).... const initialStateValue = { accessTkn: "Default Value" } const usersSlice = createSlice({ name: "users", initialState: initialStateValue, reducers:{}, myAsyncResponse: null, extraReducers:{ [fetchUsersToken.fulfilled]: (state, action) => { state.myAsyncResponse = action.payload; }, } }) export const tokenUpdated = usersSlice.actions; export default usersSlice.reducer; – Lakshmi Dec 23 '22 at 11:12
  • No, that is still very outdated - you should use the builder notation with `createSlice`. Please follow the official tutorial I linked above. – phry Dec 23 '22 at 12:44
  • As for your error: Your initial value is called `state.accessTkn`, but then you modify `state.myAsyncResponse` - so of course only that will change, not `state.accessTkn`. – phry Dec 23 '22 at 12:55
  • Can you please let me know where I'm doing wrong @phry – Lakshmi Jan 02 '23 at 05:37
  • If you want `state.accessTkn` to change, do not write `state.myAsyncResponse = something`. Write `state.accessTkn = something` instead. – phry Jan 02 '23 at 09:50
  • Please see the answer section. I have entirely revamped my code. M getting the value if I'm not using useEefect hook( but this keeps on executing infinitely) that's my concern – Lakshmi Jan 02 '23 at 10:57