---
title: useReducer Hook
description: "Details about the useReducer React hook in ReScript"
canonical: "/docs/react/latest/hooks-reducer"
---
# useReducer
`React.useReducer` helps you express your state in an action / reducer pattern.
## Usage
```res
let (state, dispatch) = React.useReducer(reducer, initialState)
```
```js
var match = React.useReducer(reducer, initialState);
```
An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, action) => newState`, and returns the current `state` paired with a `dispatch` function `(action) => unit`.
`React.useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
**Note:** You will notice that the action / reducer pattern works especially well in ReScript due to its [immutable records](/docs/manual/latest/record), [variants](/docs/manual/latest/variant) and [pattern matching](/docs/manual/latest/pattern-matching-destructuring) features for easy expression of your action and state transitions.
## Examples
### Counter Example with `React.useReducer`
```res
// Counter.res
type action = Inc | Dec
type state = {count: int}
let reducer = (state, action) => {
switch action {
| Inc => {count: state.count + 1}
| Dec => {count: state.count - 1}
}
}
@react.component
let make = () => {
let (state, dispatch) = React.useReducer(reducer, {count: 0})
<>
{React.string("Count:" ++ Belt.Int.toString(state.count))}
>
}
```
```js
function reducer(state, action) {
if (action) {
return {
count: state.count - 1 | 0
};
} else {
return {
count: state.count + 1 | 0
};
}
}
function Counter(Props) {
var match = React.useReducer(reducer, {
count: 0
});
var dispatch = match[1];
return React.createElement(React.Fragment, undefined, "Count:" + String(match[0].count), React.createElement("button", {
onClick: (function (param) {
return Curry._1(dispatch, /* Dec */1);
})
}, "-"), React.createElement("button", {
onClick: (function (param) {
return Curry._1(dispatch, /* Inc */0);
})
}, "+"));
}
```
> React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
### Basic Todo List App with More Complex Actions
You can leverage the full power of variants to express actions with data payloads to parametrize your state transitions:
```res
// TodoApp.res
type todo = {
id: int,
content: string,
completed: bool,
}
type action =
| AddTodo(string)
| RemoveTodo(int)
| ToggleTodo(int)
type state = {
todos: array,
nextId: int,
}
let reducer = (state, action) =>
switch action {
| AddTodo(content) =>
let todos = Js.Array2.concat(
state.todos,
[{id: state.nextId, content: content, completed: false}],
)
{todos: todos, nextId: state.nextId + 1}
| RemoveTodo(id) =>
let todos = Js.Array2.filter(state.todos, todo => todo.id !== id)
{...state, todos: todos}
| ToggleTodo(id) =>
let todos = Belt.Array.map(state.todos, todo =>
if todo.id === id {
{
...todo,
completed: !todo.completed,
}
} else {
todo
}
)
{...state, todos: todos}
}
let initialTodos = [{id: 1, content: "Try ReScript & React", completed: false}]
@react.component
let make = () => {
let (state, dispatch) = React.useReducer(
reducer,
{todos: initialTodos, nextId: 2},
)
let todos = Belt.Array.map(state.todos, todo =>