# Immer
>
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/api.md
# Path: api.md
---
id: api
title: API overview
---
| Exported name | Description | Section |
| --- | --- | --- |
| `produce` | The core API of Immer: `import {produce} from "immer"` | [Produce](./produce.mdx) |
| `applyPatches` | Given a base state or draft, and a set of patches, applies the patches | [Patches](./patches.mdx) |
| `castDraft` | Converts any immutable type to its mutable counterpart. This is just a cast and doesn't actually do anything. | [TypeScript](./typescript.mdx) |
| `castImmutable` | Converts any mutable type to its immutable counterpart. This is just a cast and doesn't actually do anything. | [TypeScript](./typescript.mdx) |
| `createDraft` | Given a base state, creates a mutable draft for which any modifications will be recorded | [Async](./async.mdx) |
| `current` | Given a draft object (doesn't have to be a tree root), takes a snapshot of the current state of the draft | [Current](./current.md) |
| `Draft` | Exposed TypeScript type to convert an immutable type to a mutable type | [TypeScript](./typescript.mdx) |
| `enableArrayMethods()` | Enables optimized array method handling for improved performance with array-heavy operations. | [Array Methods](./array-methods.md) |
| `enableMapSet()` | Enables support for `Map` and `Set` collections. | [Installation](./installation.mdx#pick-your-immer-version) |
| `enablePatches()` | Enables support for JSON patches. | [Installation](./installation#pick-your-immer-version) |
| `finishDraft` | Given an draft created using `createDraft`, seals the draft and produces and returns the next immutable state that captures all the changes | [Async](./async.mdx) |
| `freeze(obj, deep?)` | Freezes draftable objects. Returns the original object. By default freezes shallowly, but if the second argument is `true` it will freeze recursively. |
| `Immer` | constructor that can be used to create a second "immer" instance (exposing all APIs listed in this instance), that doesn't share its settings with global instance. |
| `immerable` | Symbol that can be added to a constructor or prototype, to indicate that Immer should treat the class as something that can be safely drafted | [Classes](./complex-objects.md) |
| `Immutable` | Exposed TypeScript type to convert mutable types to immutable types | |
| `isDraft` | Returns true if the given object is a draft object | |
| `isDraftable` | Returns true if Immer is capable of turning this object into a draft. Which is true for: arrays, objects without prototype, objects with `Object` as their prototype, objects that have the `immerable` symbol on their constructor or prototype | |
| `nothing` | Value that can be returned from a recipe, to indicate that the value `undefined` should be produced | [Return](./return.mdx) |
| `original` | Given a draft object (doesn't have to be a tree root), returns the original object at the same path in the original state tree, if present | [Original](./original.md) |
| `Patch` | Exposed TypeScript type, describes the shape of an (inverse) patch object | [Patches](./patches.mdx) |
| `produceWithPatches` | Works the same as `produce`, but instead of just returning the produced object, it returns a tuple, consisting of `[result, patches, inversePatches]`. | [Patches](./patches.mdx) |
| `setAutoFreeze` | Enables / disables automatic freezing of the trees produces. By default enabled. | [Freezing](./freezing.mdx) |
| `setUseStrictShallowCopy` | Can be used to enable strict shallow copy. If enable, immer copies non-enumerable properties as much as possible. | [Classes](./complex-objects.md) |
| `setUseStrictIteration` | Controls iteration behavior: pass `false` for loose iteration (enumerable properties only, better performance) or `true` for strict iteration (includes symbols and non-enumerable properties). By default disabled (loose iteration). | |
## Importing immer
In most cases, the only thing you need to import from Immer is `produce`:
```javascript
import {produce} from "immer"
```
Note that in older versions, `produce` was also available as default export (e.g. `import produce from "immer"` was also valid, but that is no longer the case to improve eco system compatibility.
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/array-methods.md
# Path: array-methods.md
---
id: array-methods
title: Array Methods Plugin
---
## Overview
The Array Methods Plugin (`enableArrayMethods()`) optimizes array operations within Immer producers by avoiding unnecessary Proxy creation during iteration. This provides significant performance improvements for array-heavy operations.
**Why does this matter?** Without the plugin, every array element access during iteration (e.g., in `filter`, `find`, `slice`) creates a Proxy object. For a 1000-element array, this means 1000+ proxy trap invocations just to iterate. With the plugin enabled, callbacks receive base (non-proxied) values, and proxies are only created as needed for mutation tracking.
## Installation
Enable the plugin once at your application's entry point:
```javascript
import {enableArrayMethods} from "immer"
enableArrayMethods()
```
This adds approximately **2KB** to your bundle size.
## Mutating Methods
These methods modify the array in-place and operate directly on the draft's internal copy without creating per-element proxies:
| Method | Returns | Description |
| ----------- | ---------------- | ------------------------------------- |
| `push()` | New length | Adds elements to the end |
| `pop()` | Removed element | Removes and returns the last element |
| `shift()` | Removed element | Removes and returns the first element |
| `unshift()` | New length | Adds elements to the beginning |
| `splice()` | Removed elements | Adds/removes elements at any position |
| `sort()` | The draft array | Sorts elements in place |
| `reverse()` | The draft array | Reverses the array in place |
```javascript
import {produce, enableArrayMethods} from "immer"
enableArrayMethods()
const base = {items: [3, 1, 4, 1, 5]}
const result = produce(base, draft => {
draft.items.push(9) // Adds 9 to end
draft.items.sort() // Sorts: [1, 1, 3, 4, 5, 9]
draft.items.reverse() // Reverses: [9, 5, 4, 3, 1, 1]
})
```
## Non-Mutating Methods
Non-mutating methods are categorized based on what they return:
### Subset Operations (Return Drafts)
These methods select items that exist in the original array and **create draft proxies** for the returned items. The callbacks receive **base values** (the optimization), but the **returned array** contains newly created draft proxies that point back to the original positions. **Mutations to returned items WILL affect the draft state.**
| Method | Returns | Drafts? |
| ------------ | ---------------------------------- | ------- |
| `filter()` | Array of matching items | ✅ Yes |
| `slice()` | Array of items in range | ✅ Yes |
| `find()` | First matching item or `undefined` | ✅ Yes |
| `findLast()` | Last matching item or `undefined` | ✅ Yes |
```javascript
const base = {
items: [
{id: 1, value: 10},
{id: 2, value: 20},
{id: 3, value: 30}
]
}
const result = produce(base, draft => {
// filter returns drafts - mutations track back to original
const filtered = draft.items.filter(item => item.value > 15)
filtered[0].value = 999 // This WILL affect draft.items[1]
// find returns a draft - mutations track back
const found = draft.items.find(item => item.id === 3)
if (found) {
found.value = 888 // This WILL affect draft.items[2]
}
// slice returns drafts
const sliced = draft.items.slice(0, 2)
sliced[0].value = 777 // This WILL affect draft.items[0]
})
console.log(result.items[0].value) // 777
console.log(result.items[1].value) // 999
console.log(result.items[2].value) // 888
```
### Transform Operations (Return Base Values)
These methods create **new arrays** that may include external items or restructured data. They return **base values**, NOT drafts. **Mutations to returned items will NOT track back to the draft state.**
| Method | Returns | Drafts? |
| ---------- | ------------------- | ------- |
| `concat()` | New combined array | ❌ No |
| `flat()` | New flattened array | ❌ No |
```javascript
const base = {items: [{id: 1, value: 10}]}
const result = produce(base, draft => {
// concat returns base values - mutations DON'T track
const concatenated = draft.items.concat([{id: 2, value: 20}])
concatenated[0].value = 999 // This will NOT affect draft.items[0]
// To actually use concat results, assign them:
draft.items = draft.items.concat([{id: 2, value: 20}])
})
// Original unchanged because concat result wasn't assigned
console.log(result.items[0].value) // 10 (unchanged)
```
**Why the distinction?**
- **Subset operations** (`filter`, `slice`, `find`) select items that exist in the original array. Returning drafts allows mutations to propagate back to the source.
- **Transform operations** (`concat`, `flat`) create new data structures that may include external items or restructured data, making draft tracking impractical.
### Primitive-Returning Methods
These methods return primitive values (numbers, booleans, strings). No tracking issues since primitives aren't draftable:
| Method | Returns |
| ------------------ | -------------------- |
| `indexOf()` | Number (index or -1) |
| `lastIndexOf()` | Number (index or -1) |
| `includes()` | Boolean |
| `some()` | Boolean |
| `every()` | Boolean |
| `findIndex()` | Number (index or -1) |
| `findLastIndex()` | Number (index or -1) |
| `join()` | String |
| `toString()` | String |
| `toLocaleString()` | String |
```javascript
const base = {
items: [
{id: 1, active: true},
{id: 2, active: false}
]
}
const result = produce(base, draft => {
const index = draft.items.findIndex(item => item.id === 2)
const hasActive = draft.items.some(item => item.active)
const allActive = draft.items.every(item => item.active)
console.log(index) // 1
console.log(hasActive) // true
console.log(allActive) // false
})
```
## Methods NOT Overridden
The following methods are **not** intercepted by the plugin and work through standard Proxy behavior. Callbacks receive drafts, and mutations track normally:
| Method | Description |
| --------------- | --------------------------------- |
| `map()` | Transform each element |
| `flatMap()` | Map then flatten |
| `forEach()` | Execute callback for each element |
| `reduce()` | Reduce to single value |
| `reduceRight()` | Reduce from right to left |
```javascript
const base = {
items: [
{id: 1, value: 10, nested: {count: 0}},
{id: 2, value: 20, nested: {count: 0}}
]
}
const result = produce(base, draft => {
// forEach receives drafts - mutations work normally
draft.items.forEach(item => {
item.value *= 2
})
// map is NOT overridden - callbacks receive drafts
// The returned array items are also drafts (extracted from draft.items)
const mapped = draft.items.map(item => item.nested)
// Mutations to the result array propagate back
mapped[0].count = 999 // ✅ This affects draft.items[0].nested.count
})
console.log(result.items[0].nested.count) // 999
```
## Callback Behavior
For overridden methods, callbacks receive **base values** (not drafts). This is the core optimization - it avoids creating proxies for every element during iteration.
```javascript
const base = {
items: [
{id: 1, value: 10},
{id: 2, value: 20}
]
}
produce(base, draft => {
draft.items.filter(item => {
// `item` is a base value here, NOT a draft
// Reading properties works fine
return item.value > 15
// But direct mutation here won't be tracked:
// item.value = 999 // ❌ Won't affect draft
})
// Instead, use the returned draft:
const filtered = draft.items.filter(item => item.value > 15)
filtered[0].value = 999 // ✅ This works because filtered[0] is a draft
})
```
## Method Return Behavior Summary
| Category | Methods | Returns | Mutations Track? |
| --- | --- | --- | --- |
| **Subset** | `filter`, `slice`, `find`, `findLast` | Draft proxies | ✅ Yes |
| **Transform** | `concat`, `flat` | Base values | ❌ No |
| **Primitive** | `indexOf`, `includes`, `some`, `every`, `findIndex`, `findLastIndex`, `lastIndexOf`, `join`, `toString`, `toLocaleString` | Primitives | N/A |
| **Mutating** | `push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse` | Various | ✅ Yes (modifies draft) |
| **Not Overridden** | `map`, `flatMap`, `forEach`, `reduce`, `reduceRight` | Standard behavior | ✅ Yes (callbacks get drafts) |
## When to Use
Enable the Array Methods Plugin when:
- Your application has significant array iteration within producers
- You frequently use methods like `filter`, `find`, `some`, `every` on large arrays
- Performance profiling shows array operations as a bottleneck
The plugin is most beneficial for:
- Large arrays (100+ elements)
- Frequent producer calls with array operations
- Read-heavy operations (filtering, searching) where most elements aren't modified
## Performance Benefit
**Without the plugin:**
- Every array element access during iteration creates a Proxy
- A `filter()` on 1000 elements = 1000+ proxy creations
**With the plugin:**
- Callbacks receive base values directly
- Proxies only created for the specific elements you actually mutate, or that match filtering predicates
```javascript
// Without plugin: ~3000+ proxy trap invocations
// With plugin: ~10-20 proxy trap invocations
const result = produce(largeState, draft => {
const filtered = draft.items.filter(x => x.value > threshold)
// Only items you mutate get proxied
filtered.forEach(item => {
item.processed = true
})
})
```
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/built-with.md
# Path: built-with.md
---
id: built-with
title: Built with Immer
---
- [react-copy-write](https://github.com/aweary/react-copy-write) _Immutable state with a mutable API_
- [redux-toolkit](https://github.com/reduxjs/redux-toolkit) _The official, opinionated, batteries-included toolset for efficient Redux development_
- [immer based handleActions](https://gist.github.com/kitze/fb65f527803a93fb2803ce79a792fff8) _Boilerplate free actions for Redux_
- [redux-box](https://github.com/anish000kumar/redux-box) _Modular and easy-to-grasp redux based state management, with least boilerplate_
- [quick-redux](https://github.com/jeffreyyoung/quick-redux) _tools to make redux development quicker and easier_
- [bey](https://github.com/jamiebuilds/bey) _Simple immutable state for React using Immer_
- [cool-store](https://github.com/Maxvien/cool-store) _CoolStore is an immutable state store built on top of ImmerJS and RxJS_
- [immer-wieder](https://github.com/drcmda/immer-wieder#readme) _State management lib that combines React 16 Context and immer for Redux semantics_
- [robodux](https://github.com/neurosnap/robodux) _flexible way to reduce redux boilerplate_
- [immer-reducer](https://github.com/epeli/immer-reducer) _Type-safe and terse React (useReducer()) and Redux reducers with Typescript_
- [redux-ts-utils](https://github.com/knpwrs/redux-ts-utils) _Everything you need to create type-safe applications with Redux with a strong emphasis on simplicity_
- [react-state-tree](https://github.com/suchipi/react-state-tree) _Drop-in replacement for useState that persists your state into a redux-like state tree_
- [redux-immer](https://github.com/salvoravida/redux-immer) _is used to create an equivalent function of Redux combineReducers that works with `immer` state. Like `redux-immutable` but for `immer`_
- [ngrx-wieder](https://github.com/nilsmehlhorn/ngrx-wieder) _Lightweight yet configurable solution for implementing undo-redo in Angular apps on top of NgRx and Immer_
- [immer-yjs](https://github.com/sep2/immer-yjs) _Combine `immer` with CRDT library `y.js` for easy json data manipulation_
- [immerhin](https://github.com/webstudio-is/immerhin) Sync state with undo/redo
- ... and [many more](https://www.npmjs.com/browse/depended/immer)
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/complex-objects.md
# Path: complex-objects.md
---
id: complex-objects
title: Classes
---
Plain objects (objects without a prototype), arrays, `Map`s and `Set`s are always drafted by Immer. Every other object must use the `immerable` symbol to mark itself as compatible with Immer. When one of these objects is mutated within a producer, its prototype is preserved between copies.
```js
import {immerable} from "immer"
class Foo {
[immerable] = true // Option 1
constructor() {
this[immerable] = true // Option 2
}
}
Foo[immerable] = true // Option 3
```
### Example
```js
import {immerable, produce} from "immer"
class Clock {
[immerable] = true
constructor(hour, minute) {
this.hour = hour
this.minute = minute
}
get time() {
return `${this.hour}:${this.minute}`
}
tick() {
return produce(this, draft => {
draft.minute++
})
}
}
const clock1 = new Clock(12, 10)
const clock2 = clock1.tick()
console.log(clock1.time) // 12:10
console.log(clock2.time) // 12:11
console.log(clock2 instanceof Clock) // true
```
### Semantics in detail
The semantics on how classes are drafted are as follows:
1. A draft of a class is a fresh object but with the same prototype as the original object.
1. When creating a draft, Immer will copy all _own_ properties from the base to the draft.This includes (in strict mode) non-enumerable and symbolic properties.
1. _Own_ getters will be invoked during the copy process, just like `Object.assign` would.
1. Inherited getters and methods will remain as is and be inherited by the draft, as they are stored on the prototype which is untouched.
1. Immer will not invoke constructor functions.
1. The final instance will be constructed with the same mechanism as the draft was created.
1. Only getters that have a setter as well will be writable in the draft, as otherwise the value can't be copied back.
Because Immer will dereference own getters of objects into normal properties, it is possible to use objects that use getter/setter traps on their fields, like MobX and Vue do.
Note that, by default, Immer does not strictly handle object's non-enumerable properties such as getters/setters for performance reason. If you want this behavior to be strict, you can opt-in with `useStrictShallowCopy(config)`. Use `true` to always copy strict, or `"class_only"` to only copy class instances strictly but use the faster loose copying for plain objects. The default is `false`. (Remember, regardless of strict mode, own getters / setters are always copied _by value_. There is currently no config to copy descriptors as-is. Feature request / PR welcome).
Immer does not support exotic / engine native objects such as DOM Nodes or Buffers, nor is subclassing Map, Set or arrays supported and the `immerable` symbol can't be used on them.
So when working for example with `Date` objects, you should always create a new `Date` instance instead of mutating an existing `Date` object.
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/current.md
# Path: current.md
---
id: current
title: Extracting the current state from a draft
sidebar_label: Current
---
Immer exposes a named export `current` that creates a copy of the current state of the draft. This can be very useful for debugging purposes (as those objects won't be Proxy objects and not be logged as such). Also, references to `current` can be safely leaked from a produce function. Put differently, `current` provides a snapshot of the current state of a draft.
Objects generated by `current` work similar to the objects created by produce itself.
1. Unmodified objects will be structurally shared with the original objects.
1. If no changes are made to a draft, generally it holds that `original(draft) === current(draft)`, but this is not guaranteed.
1. Future changes to the draft won't be reflected in the object produced by `current` (except for references to undraftable objects)
1. Unlike `produce` objects created by `current` will _not_ be frozen.
Use `current` sparingly, it can be a potentially expensive operation, especially when using ES5.
Note that `current` cannot be invoked on objects that aren't drafts.
### Example
The following example shows the effect of `current` (and `original`):
```js
const base = {
x: 0
}
const next = produce(base, draft => {
draft.x++
const orig = original(draft)
const copy = current(draft)
console.log(orig.x)
console.log(copy.x)
setTimeout(() => {
// this will execute after the produce has finished!
console.log(orig.x)
console.log(copy.x)
}, 100)
draft.x++
console.log(draft.x)
})
console.log(next.x)
// This will print
// 0 (orig.x)
// 1 (copy.x)
// 2 (draft.x)
// 2 (next.x)
// 0 (after timeout, orig.x)
// 1 (after timeout, copy.x)
```
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/faq.md
# Path: faq.md
---
id: faq
title: Frequently Asked Questions
sidebar_label: FAQ
---
## Q: How does Immer work?
Read the (second part of the) [introduction blog](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3).
## Q: Does Immer use structural sharing? So that my selectors can be memoized and such?
A: Yes
## Q: Does Immer support deep updates?
A: Yes
## Q: I can't rely on Proxies being present on my target environments. Can I use Immer?
A: Yes - [view details](./installation.mdx#immer-on-older-javascript-environments)
## Q: Can I typecheck my data structures when using Immer?
A: Yes
## Q: Can I store `Date` objects, functions etc in my state tree when using Immer?
A: Yes
## Q: Can I use Maps and Sets?
A: Yes
## Q: Is it fast?
A: Yes
## Q: Idea! Can Immer freeze the state for me?
A: Yes
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/introduction.md
# Path: introduction.md
---
id: introduction
title: Introduction to Immer
sidebar_label: Introduction
slug: /
---
# Immer
Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way.
> Immer is life-changing as a JS dev, and I'm not even exaggerating :) Like, it's right up there with Prettier in terms of "wow this package is amazing, how did I ever live without it?" --Mark Erikson, (the) Redux Maintainer, @replayio
Winner of the "Breakthrough of the year" [React open source award](https://osawards.com/react/) and "Most impactful contribution" [JavaScript open source award](https://osawards.com/javascript/) in 2019.
---
- Introduction blogpost: [Immer: Immutability the easy way](https://medium.com/@mweststrate/introducing-immer-immutability-the-easy-way-9d73d8f71cb3)
- Short Egghead.io lesson covering the Immer essentials: [Simplify creating immutable data trees with Immer (7m)](https://egghead.io/lessons/redux-simplify-creating-immutable-data-trees-with-immer)
- Free in-depth Egghead.io course: [Immutable JavaScript Data Structures with Immer (58m)](https://egghead.io/courses/immutable-javascript-data-structures-with-immer)
---
### Immer simplifies handling immutable data structures
Immer can be used in any context in which immutable data structures need to be used. For example in combination with React state, React or Redux reducers, or configuration management. Immutable data structures allow for (efficient) change detection: if the reference to an object didn't change, the object itself did not change. In addition, it makes cloning relatively cheap: Unchanged parts of a data tree don't need to be copied and are shared in memory with older versions of the same state.
Generally speaking, these benefits can be achieved by making sure you never change any property of an object, array or map, but by always creating an altered copy instead. In practice this can result in code that is quite cumbersome to write, and it is easy to accidentally violate those constraints. Immer will help you to follow the immutable data paradigm by addressing these pain points:
1. Immer will detect accidental mutations and throw an error.
2. Immer will remove the need for the typical boilerplate code that is needed when creating deep updates to immutable objects: Without Immer, object copies need to be made by hand at every level. Typically by using a lot of `...` spread operations. When using Immer, changes are made to a `draft` object, that records the changes and takes care of creating the necessary copies, without ever affecting the original object.
3. When using Immer, you don't need to learn dedicated APIs or data structures to benefit from the paradigm. With Immer you'll use plain JavaScript data structures, and use the well-known mutable JavaScript APIs, but safely.
### A quick example for comparison
```javascript
const baseState = [
{
title: "Learn TypeScript",
done: true
},
{
title: "Try Immer",
done: false
}
]
```
Imagine we have the above base state, and we'll need to update the second todo, and add a third one. However, we don't want to mutate the original `baseState`, and we want to avoid deep cloning as well (to preserve the first todo).
#### Without Immer
Without Immer, we'll have to carefully shallow copy every level of the state structure that is affected by our change:
```javascript
const nextState = baseState.slice() // shallow clone the array
nextState[1] = {
// replace element 1...
...nextState[1], // with a shallow clone of element 1
done: true // ...combined with the desired update
}
// since nextState was freshly cloned, using push is safe here,
// but doing the same thing at any arbitrary time in the future would
// violate the immutability principles and introduce a bug!
nextState.push({title: "Tweet about it"})
```
#### With Immer
With Immer, this process is more straightforward. We can leverage the `produce` function, which takes as first argument the state we want to start from, and as second argument we pass a function, called the _recipe_, that is passed a `draft` to which we can apply straightforward mutations. Those mutations are recorded and used to produce the next state once the recipe is done. `produce` will take care of all the necessary copying, and protect against future accidental modifications as well by freezing the data.
```javascript
import {produce} from "immer"
const nextState = produce(baseState, draft => {
draft[1].done = true
draft.push({title: "Tweet about it"})
})
```
Looking for Immer in combination with React? Feel free to skip ahead to the [React + Immer](example-setstate) page.
### How Immer works
The basic idea is that with Immer you will apply all your changes to a temporary _draft_, which is a proxy of the _currentState_. Once all your mutations are completed, Immer will produce the _nextState_ based on the mutations to the draft state. This means that you can interact with your data by simply modifying it while keeping all the benefits of immutable data.

Using Immer is like having a personal assistant. The assistant takes a letter (the current state) and gives you a copy (draft) to jot changes onto. Once you are done, the assistant will take your draft and produce the real immutable, final letter for you (the next state).
Head to the [next section](./produce.mdx) to further dive into `produce`.
## Benefits
- Follow the immutable data paradigm, while using normal JavaScript objects, arrays, Sets and Maps. No new APIs or "mutation patterns" to learn!
- Strongly typed, no string based paths selectors etc.
- Structural sharing out of the box
- Object freezing out of the box
- Deep updates are a breeze
- Boilerplate reduction. Less noise, more concise code.
- First class support for JSON patches
- Small: 3KB gzipped
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/map-set.md
# Path: map-set.md
---
id: map-set
title: Map and Set
---
_⚠ Since version 6 support for `Map`s and `Set`s has to be enabled explicitly by calling [`enableMapSet()`](./installation.mdx#pick-your-immer-version) once when starting your application._
Plain objects, arrays, `Map`s and `Set`s are always drafted by Immer. An example of using Maps with immer:
```javascript
test("Producers can update Maps", () => {
const usersById_v1 = new Map()
const usersById_v2 = produce(usersById_v1, draft => {
// Modifying a map results in a new map
draft.set("michel", {name: "Michel Weststrate", country: "NL"})
})
const usersById_v3 = produce(usersById_v2, draft => {
// Making a change deep inside a map, results in a new map as well!
draft.get("michel").country = "UK"
})
// We got a new map each time!
expect(usersById_v2).not.toBe(usersById_v1)
expect(usersById_v3).not.toBe(usersById_v2)
// With different content obviously
expect(usersById_v1).toMatchInlineSnapshot(`Map {}`)
expect(usersById_v2).toMatchInlineSnapshot(`
Map {
"michel" => Object {
"country": "NL",
"name": "Michel Weststrate",
},
}
`)
expect(usersById_v3).toMatchInlineSnapshot(`
Map {
"michel" => Object {
"country": "UK",
"name": "Michel Weststrate",
},
}
`)
// The old one was never modified
expect(usersById_v1.size).toBe(0)
// And trying to change a Map outside a producers is going to: NO!
expect(() => usersById_v3.clear()).toThrowErrorMatchingInlineSnapshot(
`"This object has been frozen and should not be mutated"`
)
})
```
Maps and Sets that are produced by Immer will be made artificially immutable. This means that they will throw an exception when trying mutative methods like `set`, `clear` etc. outside a producer.
_Note: The **keys** of a map are never drafted! This is done to avoid confusing semantics and keep keys always referentially equal_
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/original.md
# Path: original.md
---
id: original
title: Extracting the original state from a draft
sidebar_label: Original
---
Immer exposes a named export `original` that will get the original object from the proxied instance inside `produce` (or return `undefined` for unproxied values). A good example of when this can be useful is when searching for nodes in a tree-like state using strict equality.
```js
import {original, produce} from "immer"
const baseState = {users: [{name: "Richie"}]}
const nextState = produce(baseState, draftState => {
original(draftState.users) // is === baseState.users
})
```
Just want to know if a value is a proxied instance? Use the `isDraft` function! Note that `original` cannot be invoked on objects that aren't drafts.
```js
import {isDraft, produce} from "immer"
const baseState = {users: [{name: "Bobby"}]}
const nextState = produce(baseState, draft => {
isDraft(draft) // => true
isDraft(draft.users) // => true
isDraft(draft.users[0]) // => true
})
isDraft(nextState) // => false
```
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/other-lang.md
# Path: other-lang.md
---
id: other-lang
title: Porting to other languages
---
Immer has been ported to other programming languages.
|Language|Link|
|---|---|
|Java|[Jimmer](https://babyfish-ct.github.io/jimmer-doc/)|
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/pitfalls.md
# Path: pitfalls.md
---
id: pitfalls
title: Pitfalls
---
### Performance tips
For performance tips, see [Performance Tips](./performance.mdx#performance-tips).
### Don't reassign the recipe argument
Never reassign the `draft` argument (example: `draft = myCoolNewState`). Instead, either modify the `draft` or return a new state. See [Returning data from producers](./return.mdx).
### Immer only supports unidirectional trees
Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, there should be no circular references. There should be exactly one path from the root to any node of the tree.
### Never explicitly return `undefined` from a producer
It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer.
### Don't mutate exotic objects
Immer [does not support exotic objects](https://github.com/immerjs/immer/issues/504) such as window.location.
### Classes should be made draftable or not mutated
You will need to enable your own classes to work properly with Immer. For docs on the topic, check out the section on [working with complex objects](./complex-objects.md).
### Only valid indices and length can be mutated on Arrays
For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays.
### Data not originating from the state will never be drafted
Note that data that comes from the closure, and not from the base state, will never be drafted, even when the data has become part of the new draft.
```javascript
function onReceiveTodo(todo) {
const nextTodos = produce(todos, draft => {
draft.todos[todo.id] = todo
// Note, because 'todo' is coming from external, and not from the 'draft',
// it isn't draft so the following modification affects the original todo!
draft.todos[todo.id].done = true
// The reason for this, is that it means that the behavior of the 2 lines above
// is equivalent to code, making this whole process more consistent
todo.done = true
draft.todos[todo.id] = todo
})
}
```
### Immer patches are not necessarily optimal
The set of patches generated by Immer should be correct, that is, applying them to an equal base object should result in the same end state. However Immer does not guarantee the generated set of patches will be optimal, that is, the minimum set of patches possible.
### Always use the result of nested producers
Nested `produce` calls are supported, but note that `produce` will _always_ produce a new state. So even when passing a draft to a nested produce, the changes made by the inner produce won't be visible in the draft of the outer produce; those changes will only be visible in the output that the inner `produce` returns. In other words, when using nested produce, you get a draft of a draft and the result of the inner produce should be merged back into the original draft (or returned). For example `produce(state, draft => { produce(draft.user, userDraft => { userDraft.name += "!" })})` won't work as the output of the inner produce isn't used. The correct way to use nested producers is:
```javascript
produce(state, draft => {
draft.user = produce(draft.user, userDraft => {
userDraft.name += "!"
})
})
```
### Drafts aren't referentially equal
Draft objects in Immer are wrapped in `Proxy`, so you cannot use `==` or `===` to test equality between an original object and its equivalent draft (eg. when matching a specific element in an array). Instead, you can use the `original` helper:
```javascript
const remove = produce((list, element) => {
const index = list.indexOf(element) // this won't work!
const index = original(list).indexOf(element) // do this instead
if (index > -1) list.splice(index, 1)
})
const values = [a, b, c]
remove(values, a)
```
If possible, it's recommended to perform the comparison outside the `produce` function, or to use a unique identifier property like `.id` instead, to avoid needing to use `original`.
### Array Methods Plugin: Callbacks receive base values
When using the [Array Methods Plugin](./array-methods.md) (`enableArrayMethods()`), callbacks for overridden methods like `filter`, `find`, `some`, `every`, and `slice` receive **base values** (not drafts). This is the core performance optimization - it avoids creating proxies for every element during iteration.
```javascript
import {enableArrayMethods, produce} from "immer"
enableArrayMethods()
produce(state, draft => {
draft.items.filter(item => {
// `item` is a base value here, NOT a draft
// Reading works fine:
return item.value > 10
// But direct mutation here won't be tracked:
// item.value = 999 // ❌ Won't affect the draft!
})
// Instead, use the returned result (which contains drafts):
const filtered = draft.items.filter(item => item.value > 10)
filtered[0].value = 999 // ✅ This works - filtered[0] is a draft
})
```
This only applies to methods intercepted by the plugin. Methods like `map`, `forEach`, `reduce` are NOT overridden and work normally - their callbacks receive drafts.
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/resources.md
# Path: resources.md
---
id: resources
title: External resources
---
- Blog: [The Rise of Immer in React](https://www.netlify.com/blog/2018/09/12/the-rise-of-immer-in-react/)
- Blog: by Workday Prism on why they picked Immer to manage immutable state [The Search for a Strongly-Typed, Immutable State](https://medium.com/workday-engineering/workday-prism-analytics-the-search-for-a-strongly-typed-immutable-state-a09f6768b2b5)
- Blog: [Immutability in React and Redux: The Complete Guide](https://daveceddia.com/react-redux-immutability-guide/)
- Video tutorial: [Using Immer with React.setState](https://codedaily.io/screencasts/86/Immutable-Data-with-Immer-and-React-setState)
- [Talk](https://www.youtube.com/watch?v=-gJbS7YjcSo) + [slides](http://immer.surge.sh/) on Immer at React Finland 2018 by Michel Weststrate
- [ForwardJS 2019: Immutability is Changing - From Immutable.js to Immer](https://www.youtube.com/watch?v=bFuRvcAEiHg&feature=youtu.be) by [shawn swyx wang](https://twitter.com/swyx/)
- [Talk: Immer, Immutability and the Wonderful World of Proxies](https://www.youtube.com/watch?v=4Nb9Gwp2L24) + [slides](https://jsnation-proxies.surge.sh/), JSNation 2019, Michel Weststrate
- Blog: [Distributing state changes using snapshots, patches and actions](https://medium.com/@mweststrate/distributing-state-changes-using-snapshots-patches-and-actions-part-1-2811a2fcd65f)
- Blog: [Implementing Undo-Redo Functionality in Redux](https://techinscribed.com/implementing-undo-redo-functionality-in-redux-using-immer/), Sep 2019
- Blog: [Synchronized immutable state with time travel](https://dev.to/oleg008/synchronized-immutable-state-with-time-travel-2c6o), Apr 2022, by [Oleg Isonen](https://twitter.com/oleg008)
---
# Immer Documentation
# Source: https://raw.githubusercontent.com/immerjs/immer/main/website/docs/update-patterns.md
# Path: update-patterns.md
---
id: update-patterns
title: Update patterns
---
Working with immutable data, before Immer, used to mean learning all the immutable update patterns.
To help 'unlearning' those patterns here is an overview how you can leverage the built-in JavaScript APIs to update objects and collections:
### Object mutations
```javascript
import {produce} from "immer"
const todosObj = {
id1: {done: false, body: "Take out the trash"},
id2: {done: false, body: "Check Email"}
}
// add
const addedTodosObj = produce(todosObj, draft => {
draft["id3"] = {done: false, body: "Buy bananas"}
})
// delete single property
const deletedTodosObj = produce(todosObj, draft => {
delete draft["id1"]
})
// update
const updatedTodosObj = produce(todosObj, draft => {
draft["id1"].done = true
})
// replace & update in bulk
const updatedTodosObj = produce(todosObj, draft => {
Object.assign(draft, {
id1: {done: true, body: "Take out the trash"},
id2: {done: true, body: "Check Email"},
id3: {done: true, body: "Feed my cat"}
})
// reset/clear/empty
const emptyTodo = produce(todosObj, () => {
return {};
})
```
Any time a nested draft field gets a new reference or value, produce() will finish applying the immutable update and return a new reference. If you tried to mutate, but the values remained the same, Immer will bail out and return the existing reference from produce()
### Array mutations
```javascript
import {produce} from "immer"
const todosArray = [
{id: "id1", done: false, body: "Take out the trash"},
{id: "id2", done: false, body: "Check Email"}
]
// add
const addedTodosArray = produce(todosArray, draft => {
draft.push({id: "id3", done: false, body: "Buy bananas"})
})
// delete by index
const deletedTodosArray = produce(todosArray, draft => {
draft.splice(3 /*the index */, 1)
})
// update by index
const updatedTodosArray = produce(todosArray, draft => {
draft[3].done = true
})
// insert at index
const updatedTodosArray = produce(todosArray, draft => {
draft.splice(3, 0, {id: "id3", done: false, body: "Buy bananas"})
})
// remove last item
const updatedTodosArray = produce(todosArray, draft => {
draft.pop()
})
// remove first item
const updatedTodosArray = produce(todosArray, draft => {
draft.shift()
})
// add item at the beginning of the array
const addedTodosArray = produce(todosArray, draft => {
draft.unshift({id: "id3", done: false, body: "Buy bananas"})
})
// delete by id
const deletedTodosArray = produce(todosArray, draft => {
const index = draft.findIndex(todo => todo.id === "id1")
if (index !== -1) draft.splice(index, 1)
})
// update by id
const updatedTodosArray = produce(todosArray, draft => {
const index = draft.findIndex(todo => todo.id === "id1")
if (index !== -1) draft[index].done = true
})
// filtering items
const updatedTodosArray = produce(todosArray, draft => {
// creating a new state is simpler in this example
// (note that we don't need produce in this case,
// but as shown below, if the filter is not on the top
// level produce is still pretty useful)
return draft.filter(todo => todo.done)
})
// reset/clear/empty
const emptyTodo = produce(todosArray, () => {
return [];
})
```
### Nested data structures
```javascript
import {produce} from "immer"
// example complex data structure
const store = {
users: new Map([
[
"17",
{
name: "Michel",
todos: [
{
title: "Get coffee",
done: false
}
]
}
]
])
}
// updating something deeply in-an-object-in-an-array-in-a-map-in-an-object:
const nextStore = produce(store, draft => {
draft.users.get("17").todos[0].done = true
})
// filtering out all unfinished todo's
const nextStore = produce(store, draft => {
const user = draft.users.get("17")
// when filtering, creating a fresh collection is simpler than
// removing irrelevant items
user.todos = user.todos.filter(todo => !todo.done)
})
```
Note that many array operations can be used to insert multiple items at once by passing multiple arguments or using the spread operation: `todos.unshift(...items)`.
Note that when working with arrays that contain objects that are typically identified by some id, we recommend to use `Map` or index based objects (as shown above) instead of performing frequent find operations, lookup tables perform much better in general.