Search code examples
reactjsnext.jsreact-typescript

Using typescript in state slices of Reduxjs/toolkit


I'm new to the react world, i'm trying to write an app using NextJs + reduxjs/toolkit and MUI and TypeScript. I'm having trouble with the TypeScript part of the reduxjs/toolkit. This is my **store/user-preferences-silce.ts **

import { ThemeOptions } from "@mui/material";
import { createSlice } from "@reduxjs/toolkit";
import { defaultThemeOption } from "../styles/theme/themeOptions";

export type UserPreferencesSliceState = {
  theme: ThemeOptions;
};

const initialState: UserPreferencesSliceState = {
  theme: defaultThemeOption,
};

const userPreferencesSlice = createSlice({
  name: "userPreferences",
  initialState,
  reducers: {
    toggleThemeMode(state) {
      state.theme!.palette!.mode =
        state.theme!.palette!.mode === "light" ? "dark" : "light";
    },
  },
});

export const userPreferencesActions = userPreferencesSlice.actions;
export default userPreferencesSlice.reducer;

Then my store/index.ts

import { configureStore } from "@reduxjs/toolkit";
import userPreferencesReducer from "./user-preferences-slice";

const store = configureStore({
  reducer: {
    userPreferences: userPreferencesReducer,
  },
});

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

export default store;

But when i'm using the state like in my Layout.tsx

import { createTheme, ThemeProvider } from "@mui/material/styles";
import { useSelector } from "react-redux";
import { RootState } from "../../store";
import { ThemeOptions } from "@mui/material";
import { useMemo } from "react";

const Layout: React.FC = (props) => {
  const themeOptions: ThemeOptions = useSelector<RootState>(
    (state) => state.userPreferences.theme
  );
  const theme = useMemo(() => {
    return createTheme(themeOptions);
  }, [themeOptions]);

  return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
};

export default Layout;

I got this warning

Type 'unknown' not assignable to type 'ThemeOptions'

referred to this part

 const themeOptions: ThemeOptions = useSelector<RootState>(
    (state) => state.userPreferences.theme
  );

Can someone help my understand how to handle the type of the redux slices? Thank you


Solution

  • As you can see in the useSelector type definition, the hook accepts two generic types: The first one, TState, is the store state type, and the second one Selected is the selectors return type. By default, both generics are optional and the return type is unknown, that's why TypeScript complains.

    There are two possible solutions to this:

    1. you can either explicitly state the expected return type for your selector by passing ThemeOptions as the second generic type:

      const themeOptions: ThemeOptions = useSelector<RootState, ThemeOptions>(/* ... */)
      
    2. or you leave the work to TypeScript by using type inference:

      const themeOptions = useSelector((state: RootState) => state.userPreferences.theme)
      

      Take note on how I removed the generic <RootState> declaration and added its declaration to the selector parameter instead. This way, it will infer the TState from the method signature and Selected from its return type. Additionally, I removed the : ThemeOptions type declaration on the const to further infer the type, which is optional.