Typing the `useReducer` Hook in React with TypeScript
The
useReducer
useState
Understanding `useReducer` Basics
The
useReducer
dispatch
dispatch
useReducer
hook?A reducer function and an initial state.
Defining State and Action Types
The first step in typing
useReducer
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.
interface CounterState {count: number;}
Action Types
Actions typically have a
type
payload
type CounterAction =| { type: 'increment' }| { type: 'decrement' };
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
Reducer
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
useReducer
CounterState
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
payload
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 herereturn { ...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
useReducer
- Keep Actions Focused: Each action should represent a single, distinct state change.
- Use for Action Types: For string literal action types, usingcodeas constcan provide better type inference, especially when dealing with action creators.codeas const
- Type the Function: Whilecodedispatchinfers thecodeuseReducertype, explicitly typing it can sometimes improve clarity or help with custom dispatch logic.codedispatch
- Consider : For complex state objects,codeuseImmerReducercan simplify reducer logic by allowing direct mutation of draft states, which is then immutably applied.codeuseImmerReducer
Summary
Typing
useReducer
Learning Resources
The official React documentation explaining the `useReducer` hook, its parameters, and return values.
Learn about discriminated unions in TypeScript, a key pattern for typing actions in `useReducer`.
A comprehensive guide to using React with TypeScript, including sections on hooks like `useReducer`.
A practical blog post demonstrating how to effectively type the `useReducer` hook in React applications.
A video tutorial covering various React hooks, including `useReducer`, with a focus on TypeScript integration.
An in-depth explanation of the `useReducer` hook by Kent C. Dodds, with insights into its usage and benefits.
Explore TypeScript's built-in utility types, which can be helpful when defining complex state and action types.
A tutorial showing how to combine `useReducer` with React's Context API for global state management in TypeScript.
This article discusses advanced TypeScript patterns applicable to React development, including state management strategies.
Learn about `useImmerReducer`, a hook that combines `useReducer` with Immer for easier immutable state updates.