92

I'm using the useSelector(state => state.SLICE_NAME) hook from React-Redux however I'm having difficulty defining the state parameter. It is set by default as unknown so I get an error when I try to return state.SLICE_NAME (Error: Object is of type 'unknown').

How do I define the state type without having to manually create a separate state type and adding in every new state definition as they're created?

I've tried defining state as typeof store however that doesn't work.

Some code to help explain:

// navDrawer.ts

import { createSlice } from "redux-starter-kit";

// navDrawer initial state
export interface NavDrawerInitialState {
  open: boolean;
}

const navDrawerInitialState: NavDrawerInitialState = {
  open: false
};

// Create state slice
const { actions, reducer } = createSlice({
  slice: "navDrawer",
  initialState: navDrawerInitialState,
  reducers: {
    open: (state: NavDrawerInitialState) => {
      state.open = true;
    },
    close: (state: NavDrawerInitialState) => {
      state.open = false;
    }
  }
});

export const navDrawerActions = actions;
export default reducer;
// reducers.ts

import navDrawer from "./navDrawer";

const reducers = {
  navDrawer
};

export default reducers;
// store.ts

import { configureStore } from "redux-starter-kit";
import reducer from "./reducers";

const store = configureStore({
  reducer
});

export default store;
// Page.tsx

import React, { FC } from "react";
import { Provider } from "react-redux";
import store from "./store";
import ChildComponent from "./ChildComponent";

const StateProvider: FC = () => {
  return <Provider store={store}><ChildComponent /></Provider>;
};

export default StateProvider;
// ChildComponent.tsx

import React, { FC } from "react";
import { useSelector } from "react-redux";

const ChildComponent: FC = () => {
  const navDrawerState = useSelector(state => state.navDrawer); // ERROR OCCURS HERE. "state" is defined as 'unknown' so "state.navDrawer" throws an error.
  return <div>Text</div>
}

Edit: I noticed that the type definition for configureStore() contains the state as the first generic type. See screenshot below. If I can get the first generic value from EnhancedStore then I'll be able to use that to define state. Is there any way I can do this in Typescript?

enter image description here

1
  • A lot has changed since this question was asked. stackoverflow.com/a/65049635/3233055 is the "most" correct answer currently. @Jake you should select an answer. Commented Jan 5, 2021 at 3:08

17 Answers 17

139

This might not be the answer but I use it like so:

const isLoggedIn = useSelector<IRootState, boolean>(state => state.user.loggedIn);

EDIT: Or use Peter's answer which is shorter/cleaner

const isLoggedIn = useSelector((state: IRootState) => state.user.loggedIn);

FYI: To get the IRootState, there are a couple of options (from the docs):

From combineReducers():

import { combineReducers } from '@reduxjs/toolkit'
const rootReducer = combineReducers({})
export type IRootState = ReturnType<typeof rootReducer>

From configureStore():

import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ ... })
export type IRootState = ReturnType<typeof store.getState>
Sign up to request clarification or add additional context in comments.

8 Comments

This is the proper answer. People, stop upvoting the answer above and upvote this one.
couldn't help laugh at statement above. great answer it helped as well
what is IRootState?
Use @peter-v-mørch answer stackoverflow.com/a/60885506/2902063
@TuanDo, IRootState is the interface of your app's state, in my case it's the following: js import { combineReducers } from 'redux'; const rootReducer = combineReducers({ user: userReducer, foo: fooReducer, }) export type IRootState = ReturnType<typeof rootReducer>;
|
60

You can create your custom typed useSelector like so:

import {
  useSelector as useReduxSelector,
  TypedUseSelectorHook,
} from 'react-redux'
import { RootState } from 'app/redux/store'

export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector

where RootState is the type of the store, usually defined as:

export type RootState = ReturnType<typeof rootReducer>

This is the method described in the definitely typed declaration.

Don't forget to install @types/react-redux.

12 Comments

Type '{ navDrawer: Reducer<NavDrawerInitialState, AnyAction>; }' does not satisfy the constraint '(...args: any) => any'. Type '{ navDrawer: Reducer<NavDrawerInitialState, AnyAction>; }' provides no match for the signature '(...args: any): any'.
where happen that error, what's the code involved? doesn't seems related to the TypedUseSelectorHook
const state = useSelector( (getState: ReturnType<typeof reducer>) => getState.navDrawer );
@santamanno, thanks for let me know, I appended a note to redirect anyone to NearHuscarl's answer
@phry thanks for correcting me. I've deleted my answer.
|
44

Here is the suggestion (more or less) from the redux docs:

import { RootState } from 'app/redux/store';
const isLoggedIn = useSelector(state: RootState => state.user.loggedIn);

The advantage over @Federkun's answer is that it is much simpler. The advantage over @alextrastero's answer is that I don't have to specify isLoggedIn's type manually.


FYI: To get the RootState, there are a couple of options (from the docs):

From combineReducers():

import { combineReducers } from '@reduxjs/toolkit'
const rootReducer = combineReducers({})
export type RootState = ReturnType<typeof rootReducer>

From configureStore():

import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ ... })
export type RootState = ReturnType<typeof store.getState>

1 Comment

I will also mention that it requires @types/react-redux to be installed (as of today)
38

As per Redux docs, export the state as RootState in store.tsx file

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>

Then in component use it as

const navDrawerOpen = useSelector((state:RootState) => state.navDrawer.open); 

3 Comments

This is the (complete) answer. Amazing how many of the answers here are not actually answers!
This should definitely be the accepted answer. Cleanest and most correct answer I could find on the internet. Wish I could upvote more than once. Cheers!
This is the only answer that has both steps and files that need to be augmented in order to make this solution work. thank you @Pavan Jadda for this great answer.
15
  1. Create config.d.ts

  2. Define your custom state

    import 'react-redux';
    import { ApplicationState } from '@store/index';
    declare module 'react-redux' {
      interface DefaultRootState extends ApplicationState {}
    } 
    

2 Comments

That should be the actual selected answer.
This solution works like a charm but there is one small issue that the code completion of ApplicationState does not bring into DefaultRootState, how do you solve that?
7

In this video, Mark Erikson shows how to create some custom useAppSelector and useAppDispatch, to give them the correct types. Example in hooks.ts:

import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { RootState, AppDispatch } from './store';

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

And then in store.ts we have:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';

import productsReducer from '../slices/products.slice';

const store = createStore(
  productsReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

export default store;

export type AppDispatch = typeof store.dispatch; // Here we export the store's dispatch type
export type RootState = ReturnType<typeof store.getState>; // Here we export the store's state

I think it's the best and simpler approach nowadays. Hope it's useful to somebody.

1 Comment

Thanks it's in 38:06 here is a link: youtube.com/watch?v=9zySeP5vH9c#t=38m6s
4

Thanks to @Иван Яковлев, I've finally achieved my perfect solution :D

import { useSelector } from 'react-redux'
import configureStore from '../configureStore'

export const { store } = configureStore()
// We can use RootState type in every file in project
declare global {
  type RootState = ReturnType<typeof store.getState>
}

// Thanks to that you will have ability to use useSelector hook with state value
declare module 'react-redux' {
  interface DefaultRootState extends RootState { }
}

const profile = useSelector(state => state.profile) // Profile is correctly typed

Comments

3

Use typesafe-actions. It really makes life easy for you.

  1. npm install --save typesafe-actions
  2. Go to your reducer or rootReducer file and
  3. import { StateType } from 'typesafe-actions';
  4. Write your reducer function and
  5. export type Store = StateType<typeof yourReducer>;

Then

  1. Go to the ts file you want to use and
  2. import { useSelector } from 'react-redux';
  3. import {Store} from 'your-reducer-or-wherever-file'
  4. Inside your component:
  5. const store = useSelector((store: Store) => { return {students: store.manager.students} });

Notice how I used the Store type I exported from my reducer in the useSelector hook (the type of your store is what useSelector needs and you can get it easily with typesafe-actions as we just did). Also, notice how I returned an object that contains all the states I want to use. You can get creative at this point, it doesn't matter. So, the store variable in line 10 has all the states and you could as well just destructure if you want. You can read more on typesafe-actions from https://www.npmjs.com/package/typesafe-actions

1 Comment

I've tried the other listed solutions, maybe some are outdated but this is the only that worked for me, thanks
3

there is a type that 'react-redux' bundle provide

import { RootStateOrAny} from "react-redux";

const recentActivityResponse = useSelector(
    (state: RootStateOrAny) => state.dashboard.recentActivityResponse
);

Comments

3

Try this:

export interface IUser {
  userName: string;
}    

export interface IRootState {
  user: IUser;
} 

const user = useSelector<IRootState, IUser>(state => state.user);

Comments

2
import React from 'react'
import { useSelector } from 'react-redux'

type RootState = {
    auth: {
        login: string
        isAuth: boolean
    }
}

const State: RootState = {
  auth: {
     login: ''
     isAuth: false
  }
}

export function useSelectorTyped<T>(fn: (state: RootState) => T): T {
  return useSelector(fn)
}

const LoginForm = () => {
    const { login, loginError } = useSelectorTyped(state => state.auth)
    return null
}

Comments

2

I think that the best way to understand this thoroughly is the Redux docs themselves.

https://react-redux.js.org/using-react-redux/usage-with-typescript

They state

Define Typed Hooks​ While it's possible to import the RootState and AppDispatch types into each component, it's better to create pre-typed versions of the useDispatch and useSelector hooks for usage in your application. This is important for a couple reasons: For useSelector, it saves you the need to type (state: RootState) every time For useDispatch, the default Dispatch type does not know about thunks or other middleware. In order to correctly dispatch thunks, you need to use the specific customized AppDispatch type from the store that includes the thunk middleware types, and use that with useDispatch. Adding a pre-typed useDispatch hook keeps you from forgetting to import AppDispatch where it's needed.

Therefore the below should do the trick and always be used to avoid having to constantly type selectors or dispatches.

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Comments

1

DefaultRootState was removed in react-redux v8, so augmenting it no longer works. It was removed specifically because the authors of react-redux consider it an anti-pattern. Useful discussion in the original issue.

The official solution now is to create typed versions of useSelector and useDispatch and use them throughout your app: https://redux.js.org/tutorials/typescript-quick-start#define-typed-hooks

The unofficial solution, if you prefer a global declaration, is to re-map the type paths. Much thanks to this comment for the solution.

tsconfig.json:

"paths": {
  "react-redux-default": [
    "./node_modules/react-redux"
  ],
  "react-redux": [
    "./src/my-react-redux.d.ts"
  ]
},

my-react-redux.d.ts:

import { 
  TypedUseSelectorHook, 
  useDispatch as useDefaultDispatch, 
  useSelector as useDefaultSelector 
} from 'react-redux-default';

import type { AppDispatch } from './MY_PATH';
import type { ApplicationState } from './MY_PATH';

export * from 'react-redux-default';

export const useDispatch: () => AppDispatch = useDefaultDispatch;
export const useSelector: TypedUseSelectorHook<ApplicationState> = useDefaultSelector;

Comments

0

I just found in code this snippet

/**
 * This interface can be augmented by users to add default types for the root state when
 * using `react-redux`.
 * Use module augmentation to append your own type definition in a your_custom_type.d.ts file.
 * https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 */
// tslint:disable-next-line:no-empty-interface
export interface DefaultRootState {}

Comments

0

You should make an interface at store.ts that contans all the types of all state values of all files, and then import it and use it as the type of state whenever you use useState()

at foo.ts

import { createSlice } from "@reduxjs/toolkit"
const foo = createSlice({
    initialState: {value: "some value"}
})
export default foo.reducer

at store.ts

    import {foo} from "./foo.ts"
    import {configureStore} from "@reduxjs/toolkit"
    interface RootState {
      foo: {
        value: string
      }
    }
    
    export default configureStore({
        reducer: {foo: foo}
    })

at distination file

import { RootState } from "../store"
import { useState } from "react-redux"
const bar = useState((state: RootState) => state.foo.value)

Comments

0

With React Redux v9.1.0, we no longer need to use TypedUseSelectorHook or deal with awkward syntax to get typed useDispatch and useSelector hooks.

import { useDispatch, useSelector, useStore } from 'react-redux'
import type { AppDispatch, AppStore, RootState } from './store'

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
export const useAppStore = useStore.withTypes<AppStore>()

Official documentation: https://react-redux.js.org/using-react-redux/usage-with-typescript#withtypes

Comments

-3

You could just use any for the type. Just another solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.