Blog>
Snippets

Using TypeScript with Redux Toolkit 2.0

Provide an example for setting up typed hooks such as useAppDispatch and useAppSelector, including state and action typings, with Redux Toolkit 2.0.
import { configureStore } from '@reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import counterReducer from '../features/counter/counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

// Typing for the state
export type RootState = ReturnType<typeof store.getState>;
// Typing for dispatch
export type AppDispatch = typeof store.dispatch;

// Typed hooks
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
This code snippet is used to configure the Redux store using Redux Toolkit 2.0. It imports `counterReducer` from a feature slice file and adds it to the store's reducer. It also creates typed versions of the `useDispatch` and `useSelector` hooks for TypeScript, to ensure type safety throughout the application.
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from '../../app/store';

interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: state => {
      state.value += 1;
    },
    decrement: state => {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

export const selectCount = (state: RootState) => state.counter.value;

export default counterSlice.reducer;
This code snippet demonstrates creating a counter feature slice with Redux Toolkit 2.0. It defines the slice state type `CounterState`, an initial state, and the reducer functions with types for the actions. The `incrementByAmount` reducer uses `PayloadAction` to ensure that the payload is of type `number`. Finally, it exports the action creators and a selector for the `counter` value.
import * as React from 'react';
import { useAppSelector, useAppDispatch } from '../../app/hooks';
import {
  increment,
  decrement,
  incrementByAmount
} from '../../features/counter/counterSlice';

export function Counter() {
  const count = useAppSelector(state => state.counter.value);
  const dispatch = useAppDispatch();

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
      <button onClick={() => dispatch(incrementByAmount(2))}>Increment by 2</button>
    </div>
  );
}

export default Counter;
This code snippet shows a React functional component called `Counter` that uses the `useAppSelector` and `useAppDispatch` hooks to interact with the Redux state. Inside the component, it dispatches actions to increment or decrement the counter value or increment it by a specified amount. The component renders the current count and three buttons to trigger the different actions.
/* Add the Redux provider at the root of your application */
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
This snippet is for setting up the Redux provider at the root of the React application so that the Redux store is available to all the components in the app. It wraps the `App` component with the `Provider` from `react-redux` and passes in the `store` we previously configured.