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 is a senior UI engineer at Microsoft.