Group actions with a predicate
Several related action types need the same state transition.
Redux Toolkit · Module
Registers a reducer for actions selected by a custom matcher function. Matching reducers run in declaration order, after exact cases and before a default case.
builder.addMatcher registers a matcher function and a case reducer. Whenever the matcher returns true for a dispatched action, the case reducer runs; multiple matching reducers can run in definition order. Register it after exact cases and before a default case.
builder.addMatcher(matcher, reducer)| Name | Type | Description |
|---|---|---|
matcher | Matcher function | A matcher function; in TypeScript, it should be a type predicate function. |
reducer | CaseReducer | The case reducer to execute when the matcher returns true. |
A builder object, allowing further reducer registrations to be chained.
import { createReducer } from '@reduxjs/toolkit'
const initialState = { loading: false, completed: 0 }
const isCompletionAction = (action) => action.type.endsWith('/completed')
const reducer = createReducer(initialState, (builder) => {
builder
.addMatcher(isCompletionAction, (state) => {
state.completed += 1
})
.addDefaultCase((state) => {
state.loading = false
})
})This reducer increments a counter for every action whose type ends with /completed.
Several related action types need the same state transition.
A reducer should react to a property or pattern beyond action.type equality.
Several independently registered matchers should contribute to one action.
It accepts a matcher function and a case reducer. The matcher examines each incoming action, and the reducer runs when the matcher returns true.
Follow-up: What happens when an exact case and several matchers match? The exact case runs first, followed by every matching matcher in declaration order.
A matcher can be used instead of matching only the action.type property. In TypeScript, the matcher should be a type predicate so the reducer's action parameter can be inferred more precisely.
Follow-up: Why use a type predicate? It narrows the action type inside the reducer, enabling type-safe access to matcher-specific fields.
The call must follow addCase and addAsyncThunk calls, and it must precede addDefaultCase. This ordering is enforced by the builder API.
Follow-up: What occurs if no case or matcher handles the action? A default case runs when configured; otherwise the existing state is returned unchanged.
createReducercreateSlicebuilder.addCasebuilder.addAsyncThunkbuilder.addDefaultCaseisAnyOfisAllOf