LibraryTyping `useReducer` hook

Typing `useReducer` hook

Learn about Typing `useReducer` hook as part of TypeScript Full-Stack Development

Typing the `useReducer` Hook in React with TypeScript

The

code
useReducer
hook is a powerful alternative to
code
useState
for managing complex state logic in React. When combined with TypeScript, it offers enhanced type safety, preventing common bugs and improving code maintainability. This module will guide you through effectively typing your reducers and actions.

Understanding `useReducer` Basics

The

code
useReducer
hook takes two arguments: a reducer function and an initial state. It returns the current state and a
code
dispatch
function. The reducer function takes the current state and an action, and returns the new state. The
code
dispatch
function is used to send actions to the reducer.

What are the two main arguments passed to the useReducer hook?

A reducer function and an initial state.

Defining State and Action Types

The first step in typing

code
useReducer
is to define the types for your state and the actions that can be dispatched. This ensures that the reducer always receives valid state and action objects, and that the state transitions are predictable.

Let's consider a simple counter example. Our state will be a number, and our actions will be to increment or decrement it.

State Type

We can define the state type using an interface or a type alias.

typescript
interface CounterState {
count: number;
}

Action Types

Actions typically have a

code
type
property and can optionally have a
code
payload
. We can use a discriminated union for our actions to ensure type safety.

typescript
type CounterAction =
| { type: 'increment' }
| { type: 'decrement' };
Why is a discriminated union a good pattern for action types in useReducer?

It allows TypeScript to narrow down the action type within the reducer, ensuring the correct payload and properties are accessed.

Typing the Reducer Function

The reducer function itself needs to be typed. It accepts the current state and an action, and must return the new state. We can use the

code
Reducer
type from React's type definitions.

typescript
import { Reducer } from 'react';
const counterReducer: Reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
default:
return state;
}
};

Typing the `useReducer` Hook Usage

When calling

code
useReducer
, we provide the typed reducer function and the initial state, which should also match our
code
CounterState
type.

typescript
import React, { useReducer } from 'react';
const initialState: CounterState = { count: 0 };
function Counter() {
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
Count: {state.count}
);
}

By typing the state, actions, and reducer, TypeScript ensures that you dispatch the correct action types and access state properties safely, catching potential errors at compile time.

Handling Actions with Payloads

For actions that carry data, like setting a specific count, we can add a

code
payload
property to our action type. TypeScript's discriminated unions make this straightforward.

typescript
type CounterActionWithPayload =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'setCount', payload: number };
const counterReducerWithPayload: Reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'setCount':
// TypeScript knows action.payload is a number here
return { ...state, count: action.payload };
default:
return state;
}
};

The core of typing useReducer involves defining clear types for your state and actions. A discriminated union for actions, where each action type has a unique type string literal, is crucial. This allows the reducer function to safely access the correct payload based on the type property, leveraging TypeScript's control flow analysis.

📚

Text-based content

Library pages focus on text content

Best Practices and Considerations

When typing

code
useReducer
, consider these best practices:

  1. Keep Actions Focused: Each action should represent a single, distinct state change.
  2. Use
    code
    as const
    for Action Types:
    For string literal action types, using
    code
    as const
    can provide better type inference, especially when dealing with action creators.
  3. Type the
    code
    dispatch
    Function:
    While
    code
    useReducer
    infers the
    code
    dispatch
    type, explicitly typing it can sometimes improve clarity or help with custom dispatch logic.
  4. Consider
    code
    useImmerReducer
    :
    For complex state objects,
    code
    useImmerReducer
    can simplify reducer logic by allowing direct mutation of draft states, which is then immutably applied.

Summary

Typing

code
useReducer
with TypeScript involves defining types for your state, actions (often using discriminated unions), and the reducer function itself. This practice significantly enhances the robustness and maintainability of your React applications by catching type-related errors early in the development process.

Learning Resources

React Documentation: useReducer(documentation)

The official React documentation explaining the `useReducer` hook, its parameters, and return values.

TypeScript Handbook: Discriminated Unions(documentation)

Learn about discriminated unions in TypeScript, a key pattern for typing actions in `useReducer`.

React + TypeScript Cheatsheets(documentation)

A comprehensive guide to using React with TypeScript, including sections on hooks like `useReducer`.

Typing useReducer with TypeScript - Dev.to(blog)

A practical blog post demonstrating how to effectively type the `useReducer` hook in React applications.

Mastering React Hooks with TypeScript(video)

A video tutorial covering various React hooks, including `useReducer`, with a focus on TypeScript integration.

Understanding React's useReducer Hook(blog)

An in-depth explanation of the `useReducer` hook by Kent C. Dodds, with insights into its usage and benefits.

TypeScript Utility Types(documentation)

Explore TypeScript's built-in utility types, which can be helpful when defining complex state and action types.

React Context API with TypeScript and useReducer(video)

A tutorial showing how to combine `useReducer` with React's Context API for global state management in TypeScript.

Advanced TypeScript Patterns for React(blog)

This article discusses advanced TypeScript patterns applicable to React development, including state management strategies.

useImmerReducer: Simplify Reducer Logic(documentation)

Learn about `useImmerReducer`, a hook that combines `useReducer` with Immer for easier immutable state updates.