0
# Use Immer
1
2
Use Immer provides React hooks that integrate immer for immutable state management. It enables developers to write state updates using familiar mutable syntax while maintaining React's immutability requirements, making complex state transformations more readable and less error-prone.
3
4
## Package Information
5
6
- **Package Name**: use-immer
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install immer use-immer`
10
11
## Core Imports
12
13
```typescript
14
import { useImmer, useImmerReducer } from "use-immer";
15
```
16
17
CommonJS:
18
19
```javascript
20
const { useImmer, useImmerReducer } = require("use-immer");
21
```
22
23
Import individual types:
24
25
```typescript
26
import { useImmer, useImmerReducer, type DraftFunction, type ImmerHook, type ImmerReducer } from "use-immer";
27
```
28
29
Import external types used in API signatures:
30
31
```typescript
32
import type { Draft } from "immer";
33
import type { Dispatch } from "react";
34
```
35
36
## Basic Usage
37
38
```typescript
39
import React from "react";
40
import { useImmer } from "use-immer";
41
42
function PersonEditor() {
43
const [person, updatePerson] = useImmer({
44
name: "Michel",
45
age: 33,
46
address: {
47
city: "Amsterdam",
48
country: "Netherlands"
49
}
50
});
51
52
function updateName(name: string) {
53
updatePerson(draft => {
54
draft.name = name;
55
});
56
}
57
58
function moveToCity(city: string) {
59
updatePerson(draft => {
60
draft.address.city = city;
61
});
62
}
63
64
return (
65
<div>
66
<h1>{person.name} ({person.age})</h1>
67
<p>Lives in {person.address.city}, {person.address.country}</p>
68
<input
69
value={person.name}
70
onChange={e => updateName(e.target.value)}
71
/>
72
</div>
73
);
74
}
75
```
76
77
## Capabilities
78
79
### State Hook with Immer Integration
80
81
React hook similar to useState but allows mutating a draft state using immer producer functions.
82
83
```typescript { .api }
84
/**
85
* React hook similar to useState but with immer integration for immutable updates
86
* @param initialValue - Initial state value or function that returns initial state
87
* @returns Tuple of [currentState, updaterFunction]
88
*/
89
function useImmer<S = any>(initialValue: S | (() => S)): ImmerHook<S>;
90
91
type ImmerHook<S> = [S, Updater<S>];
92
93
type Updater<S> = (arg: S | DraftFunction<S>) => void;
94
95
type DraftFunction<S> = (draft: Draft<S>) => void;
96
```
97
98
The `useImmer` hook accepts either a direct value or a producer function that receives a mutable draft:
99
100
**Usage Examples:**
101
102
```typescript
103
import { useImmer } from "use-immer";
104
105
// Direct value updates (like useState)
106
const [count, setCount] = useImmer(0);
107
setCount(count + 1);
108
109
// Draft function updates (using immer)
110
const [user, updateUser] = useImmer({ name: "Alice", posts: [] });
111
updateUser(draft => {
112
draft.posts.push({ title: "New Post", content: "..." });
113
});
114
115
// Lazy initial state
116
const [expensiveState, updateExpensiveState] = useImmer(() => computeExpensiveInitialState());
117
```
118
119
### Reducer Hook with Immer Integration
120
121
Immer-powered reducer hook based on React's useReducer, allowing reducers to mutate draft state.
122
123
```typescript { .api }
124
/**
125
* Immer-powered reducer hook that allows reducers to mutate draft state
126
* @param reducer - Reducer function that operates on draft state
127
* @param initialState - Initial state value
128
* @returns Tuple of [state, dispatch]
129
*/
130
function useImmerReducer<S, A>(
131
reducer: ImmerReducer<S, A>,
132
initialState: S,
133
initializer?: undefined
134
): [S, Dispatch<A>];
135
136
/**
137
* Immer-powered reducer hook with initializer function
138
* @param reducer - Reducer function that operates on draft state
139
* @param initializerArg - Argument passed to initializer function
140
* @param initializer - Function to compute initial state
141
* @returns Tuple of [state, dispatch]
142
*/
143
function useImmerReducer<S, A, I>(
144
reducer: ImmerReducer<S, A>,
145
initializerArg: I,
146
initializer: (arg: I) => S
147
): [S, Dispatch<A>];
148
149
/**
150
* Immer-powered reducer hook with typed initializer argument
151
* @param reducer - Reducer function that operates on draft state
152
* @param initializerArg - Typed argument passed to initializer function
153
* @param initializer - Function to compute initial state from typed argument
154
* @returns Tuple of [state, dispatch]
155
*/
156
function useImmerReducer<S, A, I>(
157
reducer: ImmerReducer<S, A>,
158
initializerArg: S & I,
159
initializer: (arg: S & I) => S
160
): [S, Dispatch<A>];
161
162
type ImmerReducer<S, A> = (
163
draftState: Draft<S>,
164
action: A
165
) => void | (S extends undefined ? typeof nothing : S);
166
```
167
168
**Usage Examples:**
169
170
```typescript
171
import { useImmerReducer } from "use-immer";
172
173
// Define reducer that mutates draft state
174
function counterReducer(draft, action) {
175
switch (action.type) {
176
case "increment":
177
draft.count++;
178
break;
179
case "decrement":
180
draft.count--;
181
break;
182
case "reset":
183
return { count: 0 }; // Can still return new state
184
}
185
}
186
187
function Counter() {
188
const [state, dispatch] = useImmerReducer(counterReducer, { count: 0 });
189
190
return (
191
<div>
192
Count: {state.count}
193
<button onClick={() => dispatch({ type: "increment" })}>+</button>
194
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
195
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
196
</div>
197
);
198
}
199
200
// Complex state example
201
interface TodoState {
202
todos: Array<{ id: number; text: string; completed: boolean }>;
203
filter: "all" | "active" | "completed";
204
}
205
206
function todoReducer(draft: TodoState, action: any) {
207
switch (action.type) {
208
case "add_todo":
209
draft.todos.push({
210
id: Date.now(),
211
text: action.text,
212
completed: false
213
});
214
break;
215
case "toggle_todo":
216
const todo = draft.todos.find(t => t.id === action.id);
217
if (todo) {
218
todo.completed = !todo.completed;
219
}
220
break;
221
case "set_filter":
222
draft.filter = action.filter;
223
break;
224
}
225
}
226
```
227
228
## Types
229
230
```typescript { .api }
231
/**
232
* Function type for immer draft manipulation
233
*/
234
type DraftFunction<S> = (draft: Draft<S>) => void;
235
236
/**
237
* Updater function that accepts either a value or a draft function
238
* When passed a function, it's treated as a draft function and passed to immer's produce
239
* When passed a value, it replaces the state directly (like useState)
240
*/
241
type Updater<S> = (arg: S | DraftFunction<S>) => void;
242
243
/**
244
* Return type for useImmer hook - tuple of state and updater
245
*/
246
type ImmerHook<S> = [S, Updater<S>];
247
248
/**
249
* Reducer function type that operates on immer draft state
250
*/
251
type ImmerReducer<S, A> = (
252
draftState: Draft<S>,
253
action: A
254
) => void | (S extends undefined ? typeof nothing : S);
255
256
/**
257
* @deprecated Use ImmerReducer instead since there is already a Reducer type in @types/react
258
*/
259
type Reducer<S = any, A = any> = ImmerReducer<S, A>;
260
```
261
262
### External Types Referenced
263
264
These types are imported from external dependencies and used in the API signatures:
265
266
```typescript { .api }
267
/**
268
* From immer: Mutable proxy type for immutable state updates
269
*/
270
type Draft<T> = T; // Immer's Draft type - provides mutable interface to immutable data
271
272
/**
273
* From immer: Sentinel value for indicating deletion in draft operations
274
*/
275
const nothing: unique symbol;
276
277
/**
278
* From react: Dispatch function type for useReducer hook
279
*/
280
type Dispatch<A> = (value: A) => void;
281
```
282
283
## Dependencies
284
285
This package requires the following peer dependencies:
286
287
- **immer**: `>=8.0.0` - Core immer functionality for immutable updates
288
- **react**: `^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0` - React hooks support
289
290
The following immer types and functions are re-exported and used internally:
291
292
- `Draft<T>` - Immer draft type for mutable state proxies
293
- `nothing` - Immer sentinel value for indicating deletion
294
- `freeze` - Immer function for creating immutable objects
295
- `produce` - Immer function for creating new state from mutations
296
297
## Error Handling
298
299
The hooks follow React's error handling patterns:
300
301
- Invalid initial state will throw during component initialization
302
- Errors in draft functions are propagated as React errors
303
- Type errors occur at compile time when using TypeScript with proper typings