Home > Back-end >  Initial state for my rtk slice is not being saved in store as expected?
Initial state for my rtk slice is not being saved in store as expected?

Time:12-31

Currently learning how to use RTK with typescript. I have 2 slices, one that I've made with RTK query to fetch data (called apiSlice.ts), and another that uses createSlice to handle synchronous state change for my todo app (called snackbarSlice.ts).

The initial state I've set in the snackbarSlice are not being saved to the store at all, checked in the chrome redux devtools and also console.logged as well, but they do contain the apiSlice related info, just note the snackbar related info. Any idea what I could be doing wrong? I feel like I've been starting at this for way too long.

In the console, I get an error: Uncaught TypeError: Cannot read properties of undefined (reading 'isOpen') on CustomSnackbar (CustomSnackbar.tsx:12:1) which makes sense given the state object doesn't have an isOpen key.

Below is my code:

snackbarSlice.ts:

import { RootState } from "../store";

type SnackbarState = {
  content: string;
  isOpen: boolean;
};

const initialState: SnackbarState = {
  content: "",
  isOpen: false,
};

export const snackbarSlice = createSlice({
  name: "snackbar",
  initialState,
  reducers: {
    openSnackbar: (state, action: PayloadAction<string>) => {
      state.isOpen = true;
      state.content = action.payload;
    },
    closeSnackbar: (state) => {
      state.isOpen = false;
      state.content = "";
    },
  },
});

export const { openSnackbar, closeSnackbar } = snackbarSlice.actions;

export const selectIsSnackbarOpen = (state: RootState): boolean =>
  state.snackbar.isOpen;

export const selectSnackbarContent = (state: RootState): string =>
  state.snackbar.content;

export default snackbarSlice.reducer;

rootReducer.ts

import { apiSlice } from "./features/apiSlice";
import snackbarReducer from "./features/snackbarSlice";

const rootReducer = combineReducers({
  snackbar: snackbarReducer,
  [apiSlice.reducerPath]: apiSlice.reducer,
});

export default rootReducer;

store.ts

import { apiSlice } from "./features/apiSlice";
import rootReducer from "./rootReducer";

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
});

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;

index.tsx

import { App } from "./components/app/App";
import React from "react";
import { ApiProvider } from "@reduxjs/toolkit/dist/query/react";
import { apiSlice } from "./store/features/apiSlice";
import { Provider } from "react-redux";
import { store } from "./store/store";

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <Provider store={store}>
      <ApiProvider api={apiSlice}>
        <App />
      </ApiProvider>
    </Provider>
  </React.StrictMode>
);

customSnackbar.tsx

import { useAppDispatch, useAppSelector } from "../../hooks/reduxHooks";
import {
  closeSnackbar,
  selectIsSnackbarOpen,
  selectSnackbarContent,
} from "../../store/features/snackbarSlice";
import styles from "./CustomSnackbar.module.css";

export const CustomSnackbar = (): ReactElement => {
  const dispatch = useAppDispatch();
  const isSnackbarOpen = useAppSelector(selectIsSnackbarOpen);
  const snackbarContent = useAppSelector(selectSnackbarContent);

  const handleClose = (): void => {
    dispatch(closeSnackbar());
  };

  return (
    <div>
      {isSnackbarOpen && (
        <div className={styles["snackbar"]}>
          <div>
            <span className={styles["snackbar-btn"]} onClick={handleClose}>
              x
            </span>
            {snackbarContent}
          </div>
        </div>
      )}
    </div>
  );
};

and in any file where I need to open the snackbar, I run the following code: ispatch(openSnackbar("An error occurred while deleting task."));

Any thoughts on what I could be missing?

CodePudding user response:

The issue appears to be ApiProvider in index.tsx. That is extraneous and unnecessary since the apiSlice should already be included in the store when it's passed to Provider.

ApiProvider

Can be used as a Provider if you do not already have a Redux store.

DANGER

Using this together with an existing Redux store will cause them to conflict with each other. If you are already using Redux, please follow the instructions as shown in the Getting Started guide.

The app is using the closest Redux context/Provider, which is the ApiProvider and it doesn't include the snackbar state/reducer.

Remove the ApiProvider component.

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);
  • Related