Redux Toolkit TypeScript Quick Start
- How to set up and use Redux Toolkit and React-Redux with TypeScript
- Knowledge of ReactHooks
- Understanding ofRedux terms and concepts
- Understanding of TypeScript syntax and concepts
Introduction
Welcome to the Redux Toolkit TypeScript Quick Start tutorial!This tutorial will briefly show how to use TypeScript with Redux Toolkit and React-Redux.
This page focuses on just how to set up the TypeScript aspects . For explanations of what Redux is, how it works, and full examples of how to use Redux Toolkit,see the tutorials linked in the "Tutorials Index" page.
Redux Toolkit is already written in TypeScript, so its TS type definitions are built in.
React Redux is also written in TypeScript as of version 8, and also includes its own type definitions.
TheRedux+TS template for Create-React-App comes with a working example of these patterns already configured.
Project Setup
Define Root State and Dispatch Types
Redux Toolkit'sconfigureStore API should not need any additional typings. You will, however, want to extract theRootState type and theDispatch type so that they can be referenced as needed. Inferring these types from the store itself means that they correctly update as you add more state slices or modify middleware settings.
Since those are types, it's safe to export them directly from your store setup file such asapp/store.ts and import them directly into other files.
import{ configureStore}from'@reduxjs/toolkit'
// ...
exportconst store=configureStore({
reducer:{
posts: postsReducer,
comments: commentsReducer,
users: usersReducer
}
})
// Infer the `RootState`, `AppDispatch`, and `AppStore` types from the store itself
exporttypeRootState= ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
exporttypeAppDispatch=typeof store.dispatch
exporttypeAppStore=typeof store
Define Typed Hooks
While it's possible to import theRootState andAppDispatch types into each component, it'sbetter to create typed versions of theuseDispatch anduseSelector 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 defaultDispatchtype does not know about thunks. In order to correctly dispatch thunks, you need to use the specific customizedAppDispatchtype from the store that includes the thunk middleware types, and use that withuseDispatch. Adding a pre-typeduseDispatchhook keeps you from forgetting to importAppDispatchwhere it's needed.
Since these are actual variables, not types, it's important to define them in a separate file such asapp/hooks.ts, not the store setup file. This allows you to import them into any component file that needs to use the hooks, and avoids potential circular import dependency issues.
import{ useDispatch, useSelector}from'react-redux'
importtype{ AppDispatch, RootState}from'./store'
// Use throughout your app instead of plain `useDispatch` and `useSelector`
exportconst useAppDispatch= useDispatch.withTypes<AppDispatch>()
exportconst useAppSelector= useSelector.withTypes<RootState>()
Application Usage
Define Slice State and Action Types
Each slice file should define a type for its initial state value, so thatcreateSlice can correctly infer the type ofstate in each case reducer.
All generated actions should be defined using thePayloadAction<T> type from Redux Toolkit, which takes the type of theaction.payload field as its generic argument.
You can safely import theRootState type from the store file here. It's a circular import, but the TypeScript compiler can correctly handle that for types. This may be needed for use cases like writing selector functions.
import{ createSlice, PayloadAction}from'@reduxjs/toolkit'
importtype{ RootState}from'../../app/store'
// Define a type for the slice state
exportinterfaceCounterState{
value:number
}
// Define the initial state using that type
const initialState: CounterState={
value:0
}
exportconst counterSlice=createSlice({
name:'counter',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers:{
increment: state=>{
state.value+=1
},
decrement: state=>{
state.value-=1
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount:(state, action: PayloadAction<number>)=>{
state.value+= action.payload
}
}
})
exportconst{ increment, decrement, incrementByAmount}= counterSlice.actions
// Other code such as selectors can use the imported `RootState` type
exportconstselectCount=(state: RootState)=> state.counter.value
exportdefault counterSlice.reducer
The generated action creators will be correctly typed to accept apayload argument based on thePayloadAction<T> type you provided for the reducer. For example,incrementByAmount requires anumber as its argument.
In some cases,TypeScript may unnecessarily tighten the type of the initial state. If that happens, you can work around it by casting the initial state usingas, instead of declaring the type of the variable:
// Workaround: cast state instead of declaring variable type
const initialState={
value:0
}as CounterState
Use Typed Hooks in Components
In component files, import the pre-typed hooks instead of the standard hooks from React-Redux.
importReactfrom'react'
import{ useAppSelector, useAppDispatch}from'app/hooks'
import{ decrement, increment}from'./counterSlice'
exportfunctionCounter(){
// The `state` arg is correctly typed as `RootState` already
const count=useAppSelector(state=> state.counter.value)
const dispatch=useAppDispatch()
// omit rendering logic
}
Full Counter App Example
Here's the complete TS counter application as a running CodeSandbox:
What's Next?
We recommend going throughthe full "Redux Essentials" tutorial, which covers all of the key pieces included in Redux Toolkit, what problems they solve, and how to use them to build real-world applications.
You may also want to read throughthe "Redux Fundamentals" tutorial, which will give you a complete understanding of how Redux works, what Redux Toolkit does, and how to use it correctly.
Finally, seethe "Usage with TypeScript" page for extended details on how to use Redux Toolkit's APIs with TypeScript.