Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

basereducer is not a function - Redux Toolkit with createasyncthunk


I am using redux-toolkit to acquire accessToken from MSAL and redux-persist to persist the store in localStorage. I'm getting search results in clientlisting page. When I refresh the page it was working fine. But few minutes ago it throws me an error "Error in function eval in ./node_modules/redux-persist/es/persistReducer.js:144 baseReducer is not a function" I couldn't figure where am I doing wrong

store.js

import { configureStore } from '@reduxjs/toolkit'
import usersReducer from "./userSlice";
import storage from 'redux-persist/lib/storage';
import { persistReducer, persistStore } from 'redux-persist';
const persistConfig = { key: 'root', storage, }
const persistedReducer = persistReducer(persistConfig, usersReducer)
export const store = configureStore(
  { 
    reducer: { 
      users: persistedReducer,
    }
  })
export const persistor = persistStore(store)

userSlice.js

import { useMsal } from "@azure/msal-react";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { loginRequest } from "./authConfig";
import { msalInstance } from "./pages/index";

export const fetchUsersToken = createAsyncThunk(
  "users/fetchUsersToken",
  async (dispatch, getState) => {
    try {
      const token = await msalInstance.acquireTokenSilent(dispatch)
        .then((data) => data.accessToken)
      return token
    } catch (error) {
      return error.response.data
    }
  }
);

const usersSlice = createSlice({
  name: "users",
  initialState: {
    users: null,
    loading: true
  },
  reducers: {},
  extraReducers(builder) {
    builder
      .addCase(fetchUsersToken.pending, (state, action) => {
        state.loading = true
      })
      .addCase(fetchUsersToken.fulfilled, (state, action) => {
        state.loading = false,
        state.users = action.payload
      })
      .addCase(fetchUsersToken.rejected, (state, action) => {
        state.loading = false

      });
  }
})

export default usersSlice.reducer;

index.js

import React from "react"
import { Provider, useDispatch } from "react-redux";
import {persistor, store} from "../../store";
import Footer from "../Footer"
import { createTheme, ThemeProvider } from "@mui/material/styles"
import { PersistGate } from 'redux-persist/integration/react';
// Global styles and component-specific styles.

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

const Layout = ({ children }) => (
  <div>
    <ThemeProvider theme={theme}>
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          {children}
          <Footer/>
        </PersistGate>
      </Provider>
    </ThemeProvider>
  </div>
)

export default Layout

LandingPage.js ( where I'm dispatching the action.)

const request = {
  ...loginRequest,
  account: accounts[0]
}
store.dispatch(fetchUsersToken(request))

Here is my index.js ( where msalInstance initiated )

import React from "react"
import { Helmet } from "react-helmet"
import { PublicClientApplication } from "@azure/msal-browser"
import { MsalProvider, useMsal } from "@azure/msal-react"
import { loginRequest, msalConfig } from "../authConfig"
import PageLayout from "../components/PageLayout"
import App from "./app"
import Layout from "../components/Layout"

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

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

export default function Home() {

return (
<>
  <Helmet>
    <title>Client Engagement Lookup</title>
  </Helmet>
  <MsalProvider instance={msalInstance}>
   
    {/* <Provider store={store}> */}
       <Layout>
      <PageLayout />
      </Layout>
      {/* </Provider> */}

  </MsalProvider>
 </>
 )

}


Solution

  • After copy/pasting the code you shared into a running codesandbox I wasn't able to reproduce the error you describe, but I do see some discrepancies in the code, specifically in the userSlice.js file.

    The main discrepancy I see is that the thunk is incorrectly accessing the thunkAPI. createAsyncThunk payload creators do take two arguments, the first is the arg (e.g. the request object) that is passed to the function and the second is the thunkAPI object. Update the thunk to correctly destructure dispatch and getState from the thunkAPI object.

    export const fetchUsersToken = createAsyncThunk(
      "users/fetchUsersToken",
      async (request, { dispatch, getState }) => { // <-- destructure thunkAPI
        try {
          const { accessToken } = await msalInstance.acquireTokenSilent(request);
          return accessToken;
        } catch (error) {
          return error.response.data;
        }
      }
    );
    

    A second discrepancy I noticed was in the fetchUsersToken.fulfilled reducer case where a Comma operator was used between the lines to set the loading and users states. This doesn't really effect much though since each operand mutates the state independently, but should still be fixed for readability's and maintenance's sake.

    const usersSlice = createSlice({
      name: "users",
      initialState: {
        users: null,
        loading: true
      },
      extraReducers(builder) {
        builder
          .addCase(fetchUsersToken.pending, (state, action) => {
            state.loading = true;
          })
          .addCase(fetchUsersToken.fulfilled, (state, action) => {
            state.loading = false;        // <-- own line, expression
            state.users = action.payload; // <-- own line, expression
          })
          .addCase(fetchUsersToken.rejected, (state, action) => {
            state.loading = false;
          });
      }
    });
    
    export default usersSlice.reducer;
    

    Edit basereducer-is-not-a-function-redux-toolkit-with-createasyncthunk