TypeOfNaN

How to use the useSelector Redux hook with Typescript

Nick Scialli
May 17, 2021

Redux offers the useSelector hook to get values from your store. If you’re using Typescript, make sure you get the most out the hook by typing it correctly.

A basic store with user information

Let’s say you have a store that contains some information about a logged-in user. We’ll do a really basic store setup here with some action creators and a reducer.

import { createStore } from 'redux';

type User = {
  name: string;
  age: number;
  admin: boolean;
};

const defaultUser: User = {
  name: 'Guest',
  age: 0,
  admin: false,
};

const setName = (payload: string) =>
  ({
    type: 'SET_NAME',
    payload,
  } as const);

const setAge = (payload: number) =>
  ({
    type: 'SET_AGE',
    payload,
  } as const);

const setAdmin = (payload: boolean) =>
  ({
    type: 'SET_ADMIN',
    payload,
  } as const);

type Actions = ReturnType<typeof setName | typeof setAge | typeof setAdmin>;

function reducer(state = defaultUser, action: Actions) {
  switch (action.type) {
    case 'SET_NAME':
      return { ...state, name: action.payload };
    case 'SET_AGE':
      return { ...state, age: action.payload };
    case 'SET_ADMIN':
      return { ...state, admin: action.payload };
    default:
      return state;
  }
}

const store = createStore(reducer);

type RootState = ReturnType<typeof store.getState>;

Note that I’m deducing the type of RootState here from the return type of store.getState, which is a bit unnecessary since our state is the same as User, but in a more complex scenario this would be much more helpful.

Using the useSelector hook

We now want to display our user’s name in a Header component. Without types, we can envision using the useSelector hook like this:

import { useSelector } from 'react-redux';

export const Header = () => {
  const name = useSelector((state) => state.name);

  return <div>Welcome, {name}!</div>;
};

However, if we have our Typescript compiler to yell about implicit any, then we have a compilation error here. But furthermore, we want to get the best out of Typescript, so adding some types to useSelector to make sure we’re accessing the correct path in state and to make sure our returned value is the expected type is important to us.

To add types to useSelector, we have to pass it two Generic values: the first Generic is the type of our entire state object (recall we created RootState!) and the second Generic is the type of the returned value we expect (in this case, we expect name to be a string).

With this in mind, here’s how we type our useSelector hook:

import { useSelector } from 'react-redux';

export const Header = () => {
  const name = useSelector<RootState, string>((state) => state.name);

  return <div>Welcome, {name}!</div>;
};

Now our compiler is happy and we’re assured that we are accessing the name property on our state correctly.

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli