ln09nv2
ln09nv2

Reputation: 1343

TypeError: store.dispatch is not a function

I'm following along the Redux Writing Test guide to test my Redux/TypeScript application. I ran into TypeError: store.dispatch is not a function error.

I followed this suggestion of exporting and consuming an already configured store object but I still receive the error. What am I missing?

(my attempt)

export default function setupStore(preloadedState?: PreloadedState<RootState>) {
    return configureStore({
        reducer: persistedReducer,
        middleware: (getDefaultMiddleware) =>
            getDefaultMiddleware({
                serializableCheck: {
                    ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
                },
            }),
        preloadedState,
    });
};

Here are the files with the related code.

test-utils.tsx

import setupStore from '../store';

interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
    preloadedState?: PreloadedState<RootState>;
    store?: AppStore;
}

export function renderWithProviders(
    ui: React.ReactElement,
    {
        preloadedState = {},
        store = setupStore(preloadedState),
        ...renderOptions
    }: ExtendedRenderOptions = {},
) {
    function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
        return <Provider store={store}>{children}</Provider>;
    }

    return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}

store.ts

const persistConfig = {
    key: 'root',
    version: 1,
    storage,
};

const rootReducer = combineReducers({ user: userReducer, cart: cartReducer });
const persistedReducer = persistReducer(persistConfig, rootReducer);
export default function setupStore(preloadedState?: PreloadedState<RootState>) {
    return configureStore({
        reducer: persistedReducer,
        middleware: (getDefaultMiddleware) =>
            getDefaultMiddleware({
                serializableCheck: {
                    ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
                },
            }),
        preloadedState,
    });
};

export let persistor = persistStore(setupStore as any);
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = AppStore['dispatch'];

Navbar.tsx

const Navbar: React.FC = () => {
    const cartQuantity = useAppSelector((state: RootState) => state.cart.cartQuantity);
    const { currentUser } = useAppSelector((state: RootState) => state.user);

    return (
        <Language>EN</Language>
    );
};

Navbar.test.tsx

it('renders same span text in Navbar component', async () => {
    renderWithProviders(<Navbar />);
    const spanElement = screen.getByText(/EN/i);
    expect(spanElement).toBeInTheDocument();
});

Upvotes: 1

Views: 782

Answers (1)

Drew Reese
Drew Reese

Reputation: 202608

I think you've basically about the same issue as the post you linked to with the setupStore function and needing to call it to get an actual store object.

AppDispatch needs to be the typeof store.dispatch. Try the following where AppDispatch is "picked" from the AppStore type.

store.ts

export default function setupStore(preloadedState?: PreloadedState<RootState>) {
  return configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware({
        serializableCheck: {
          ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
        },
      }),
    preloadedState,
  });
};

export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = Pick<AppStore, "dispatch">;

index.tsx - Import setupStore and persistStore here and instantiate the store persistor objects.

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import persistStore from "redux-persist/es/persistStore";
import setupStore from "./store";

const store = setupStore();
const persistor = persistStore(store);

ReactDOM.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>,
  document.getElementById("root")
);

Edit typeerror-store-dispatch-is-not-a-function

Upvotes: 2

Related Questions