### Enable Map/Set Support and Use Produce with Map - Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/installation.mdx
Demonstrates how to enable Map and Set support in Immer (required since v6) by calling `enableMapSet()` once in the application's entrypoint. It then shows an example of using the `produce` function to immutably update a JavaScript `Map`.
```typescript
// In your application's entrypoint
import {enableMapSet} from "immer"
enableMapSet()
// ...later
import {produce} from "immer"
const usersById_v1 = new Map([
["michel", {name: "Michel Weststrate", country: "NL"}]
])
const usersById_v2 = produce(usersById_v1, draft => {
draft.get("michel").country = "UK"
})
expect(usersById_v1.get("michel").country).toBe("NL")
expect(usersById_v2.get("michel").country).toBe("UK")
```
--------------------------------
### Defining Initial State in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/introduction.md
Defines a sample array of objects representing a todo list. This state is used as the starting point for demonstrating state updates with and without Immer.
```javascript
const baseState = [
{
title: "Learn TypeScript",
done: true
},
{
title: "Try Immer",
done: false
}
]
```
--------------------------------
### Example Immer Patches (JSON)
Source: https://github.com/immerjs/immer/blob/main/website/docs/patches.mdx
This JSON snippet provides an example of the structure of patches generated by Immer. Patches follow a format similar to RFC-6902 JSON patch, but with the `path` property as an array.
```json
[
{
"op": "replace",
"path": ["profile"],
"value": {"name": "Veria", "age": 5}
},
{"op": "remove", "path": ["tags", 3]}
]
```
--------------------------------
### Using produceWithPatches to Get State and Patches (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/patches.mdx
Demonstrates the basic usage of `produceWithPatches` from Immer. It takes an initial state and a producer function, returning a tuple containing the next state, the patches applied, and the inverse patches.
```javascript
import {produceWithPatches} from "immer"
const [nextState, patches, inversePatches] = produceWithPatches(
{
age: 33
},
draft => {
draft.age++
}
)
```
--------------------------------
### Example Immer Inverse Patches (JSON)
Source: https://github.com/immerjs/immer/blob/main/website/docs/patches.mdx
This JSON snippet provides an example of the structure of inverse patches generated by Immer. Inverse patches can be applied to revert the changes made by the original patches.
```json
[
{"op": "replace", "path": ["profile"], "value": {"name": "Noa", "age": 6}},
{"op": "add", "path": ["tags", 3], "value": "kiddo"}
]
```
--------------------------------
### Example Output of produceWithPatches (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/patches.mdx
Shows the structure and content of the tuple returned by `produceWithPatches` when modifying an object with an 'age' property. The output includes the new state, the patch to apply the change, and the inverse patch to revert it.
```javascript
[
{
age: 34
},
[
{
op: "replace",
path: ["age"],
value: 34
}
],
[
{
op: "replace",
path: ["age"],
value: 33
}
]
]
```
--------------------------------
### Example: Using Immer with a Custom Class
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/complex-objects.md
Provides a complete example of a Clock class that is Immer-compatible. It shows how to use the `immerable` symbol and how to use `produce` within a class method (`tick`) to create a new, modified instance while preserving the original.
```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
```
--------------------------------
### Generating and Applying Patches with Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/patches.mdx
This JavaScript snippet demonstrates how to use Immer's patch functionality. It shows how to capture patches and inverse patches from a `produce` call and then apply those patches to a state object using `applyPatches`. It includes an example of applying forward changes and then reverting them. Requires `immer` and `enablePatches()` (for v6+).
```javascript
import {produce, applyPatches} from "immer"
// version 6
import {enablePatches} from "immer"
enablePatches()
let state = {
name: "Micheal",
age: 32
}
// Let's assume the user is in a wizard, and we don't know whether
// his changes should end up in the base state ultimately or not...
let fork = state
// all the changes the user made in the wizard
let changes = []
// the inverse of all the changes made in the wizard
let inverseChanges = []
fork = produce(
fork,
draft => {
draft.age = 33
},
// The third argument to produce is a callback to which the patches will be fed
(patches, inversePatches) => {
changes.push(...patches)
inverseChanges.push(...inversePatches)
}
)
// In the meantime, our original state is replaced, as, for example,
// some changes were received from the server
state = produce(state, draft => {
draft.name = "Michel"
})
// When the wizard finishes (successfully) we can replay the changes that were in the fork onto the *new* state!
state = applyPatches(state, changes)
// state now contains the changes from both code paths!
expect(state).toEqual({
name: "Michel", // changed by the server
age: 33 // changed by the wizard
})
// Finally, even after finishing the wizard, the user might change his mind and undo his changes...
state = applyPatches(state, inverseChanges)
expect(state).toEqual({
name: "Michel", // Not reverted
age: 32 // Reverted
})
```
--------------------------------
### Using a Class Instance with Immer's produce (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/complex-objects.md
Illustrates how to use a class instance with Immer's `produce` function. The example defines a `Clock` class marked as `immerable` and shows a `tick` method that uses `produce` to create a new `Clock` instance with an updated state, demonstrating prototype preservation.
```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
```
--------------------------------
### Managing State with useReducer and Immer's produce (React)
Source: https://github.com/immerjs/immer/blob/main/website/docs/example-setstate.mdx
Illustrates how to integrate Immer's produce function with React's useReducer hook. The reducer function is wrapped by produce, allowing mutable logic within the reducer while ensuring an immutable new state is returned. Shows examples for 'toggle' and 'add' actions. Requires react and immer.
```javascript
import React, {useCallback, useReducer} from "react"
import {produce} from "immer"
const TodoList = () => {
const [todos, dispatch] = useReducer(
produce((draft, action) => {
switch (action.type) {
case "toggle":
const todo = draft.find(todo => todo.id === action.id)
todo.done = !todo.done
break
case "add":
draft.push({
id: action.id,
title: "A new todo",
done: false
})
break
default:
break
}
}),
[
/* initial todos */
]
)
const handleToggle = useCallback(id => {
dispatch({
type: "toggle",
id
})
}, [])
const handleAdd = useCallback(() => {
dispatch({
type: "add",
id: "todo_" + Math.random()
})
}, [])
// etc
}
```
--------------------------------
### Demonstrating Immer current and original behavior in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/current.md
This JavaScript example illustrates the difference between the original base state (`original`), the snapshot of the draft at a specific point (`current`), and the mutable draft itself within an Immer producer. It shows how `current` captures the state when called and is unaffected by later draft modifications, even when accessed asynchronously after the producer finishes.
```javascript
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)
```
--------------------------------
### Performing Object Mutations with Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/update-patterns.md
Demonstrates how to use Immer's `produce` function to add, delete, and update properties on a JavaScript object immutably. The code shows examples for adding a new key-value pair, deleting an existing key, and modifying the value of a property.
```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
const deletedTodosObj = produce(todosObj, draft => {
delete draft["id1"]
})
// update
const updatedTodosObj = produce(todosObj, draft => {
draft["id1"].done = true
})
```
--------------------------------
### Comparing Draft and Original Objects with Immer's `original` (JS)
Source: https://github.com/immerjs/immer/blob/main/website/docs/pitfalls.md
Explains that Immer drafts are Proxy objects and cannot be compared for strict equality (`===`) with their original counterparts. Provides an example using the `original` helper function to retrieve the original object from a draft, enabling operations like `indexOf` that rely on referential equality.
```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)
```
--------------------------------
### Updating State with useState and Immer's produce (React)
Source: https://github.com/immerjs/immer/blob/main/website/docs/example-setstate.mdx
Demonstrates how to use Immer's produce function as the updater for React's useState hook to perform immutable updates on complex state objects or arrays without manual copying. Shows examples for toggling a todo item and adding a new one. Requires react and immer.
```javascript
import React, { useCallback, useState } from "react";
import {produce} from "immer";
const TodoList = () => {
const [todos, setTodos] = useState([
{
id: "React",
title: "Learn React",
done: true
},
{
id: "Immer",
title: "Try Immer",
done: false
}
]);
const handleToggle = useCallback((id) => {
setTodos(
produce((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done;
})
);
}, []);
const handleAdd = useCallback(() => {
setTodos(
produce((draft) => {
draft.push({
id: "todo_" + Math.random(),
title: "A new todo",
done: false
});
})
);
}, []);
return (
{*/ See CodeSandbox */}
)
}
```
--------------------------------
### Updating Nested Data Structures with Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/update-patterns.md
Shows how Immer simplifies updating deeply nested data structures, including objects, Maps, and arrays. It provides examples for modifying a property deep within the structure and filtering an array that is nested inside other objects/collections.
```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)
})
```
--------------------------------
### Using the curried produce form (Immer, JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/curried-produce.mdx
Illustrates the curried usage of `produce`. Passing only the recipe function to `produce` returns a new function that accepts the base state as its first argument, followed by any additional arguments needed by the recipe function. Simplifies function definitions.
```javascript
import {produce} from "immer"
// curried producer:
const toggleTodo = produce((draft, id) => {
const todo = draft.find(todo => todo.id === id)
todo.done = !todo.done
})
const baseState = [
/* as is */
]
const nextState = toggleTodo(baseState, "Immer")
```
--------------------------------
### Importing the produce function in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/api.md
Demonstrates the standard way to import the `produce` function from the Immer library using ES module syntax. This is the most commonly used export.
```javascript
import {produce} from "immer"
```
--------------------------------
### Verifying Immutability and Structural Sharing with produce in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/produce.mdx
Shows how to use assertions (e.g., with Jest or similar testing frameworks) to verify the behavior of `produce`. It confirms that the original `baseState` remains unchanged, the `nextState` reflects the modifications made in the recipe, and unchanged parts of the state are structurally shared between the base and next states.
```javascript
// the new item is only added to the next state,
// base state is unmodified
expect(baseState.length).toBe(2)
expect(nextState.length).toBe(3)
// same for the changed 'done' prop
expect(baseState[1].done).toBe(false)
expect(nextState[1].done).toBe(true)
// unchanged data is structurally shared
expect(nextState[0]).toBe(baseState[0])
// ...but changed data isn't.
expect(nextState[1]).not.toBe(baseState[1])
```
--------------------------------
### Using produce in a standard function (Immer, JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/curried-produce.mdx
Demonstrates a common pattern where a function takes state and other arguments, then uses `produce` internally with the provided state and a recipe function to create the next state. Requires the `produce` function from 'immer'.
```javascript
import {produce} from "immer"
function toggleTodo(state, id) {
return produce(state, draft => {
const todo = draft.find(todo => todo.id === id)
todo.done = !todo.done
})
}
const baseState = [
{
id: "JavaScript",
title: "Learn TypeScript",
done: true
},
{
id: "Immer",
title: "Try Immer",
done: false
}
]
const nextState = toggleTodo(baseState, "Immer")
```
--------------------------------
### Simplifying useReducer with useImmerReducer (React)
Source: https://github.com/immerjs/immer/blob/main/website/docs/example-setstate.mdx
Demonstrates the useImmerReducer hook from the use-immer package, which simplifies the integration of Immer with useReducer. It automatically wraps the reducer function with produce, allowing direct mutation of the draft state within the reducer logic. Requires react and use-immer.
```javascript
import React, { useCallback } from "react";
import { useImmerReducer } from "use-immer";
const TodoList = () => {
const [todos, dispatch] = useImmerReducer(
(draft, action) => {
switch (action.type) {
case "toggle":
const todo = draft.find((todo) => todo.id === action.id);
todo.done = !todo.done;
break;
case "add":
draft.push({
id: action.id,
title: "A new todo",
done: false
});
break;
default:
break;
}
},
[ /* initial todos */ ]
);
//etc
}
```
--------------------------------
### Basic Usage of produce with Array State in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/produce.mdx
Demonstrates importing the `produce` function from 'immer' and using it with an array as the base state. The recipe function receives a draft of the state, which is then mutated by pushing a new object and modifying a property of an existing object. `produce` returns a new state reflecting these changes.
```javascript
import {produce} from "immer"
const baseState = [
{
title: "Learn TypeScript",
done: true
},
{
title: "Try Immer",
done: false
}
]
const nextState = produce(baseState, draftState => {
draftState.push({title: "Tweet about it"})
draftState[1].done = true
})
```
--------------------------------
### Simplifying useState updates with useImmer (React)
Source: https://github.com/immerjs/immer/blob/main/website/docs/example-setstate.mdx
Shows how to use the useImmer hook from the use-immer package as a drop-in replacement for useState. This hook automatically wraps the state updater function with Immer's produce, simplifying the code for immutable updates. Requires react and use-immer.
```javascript
import React, { useCallback } from "react";
import { useImmer } from "use-immer";
const TodoList = () => {
const [todos, setTodos] = useImmer([
{
id: "React",
title: "Learn React",
done: true
},
{
id: "Immer",
title: "Try Immer",
done: false
}
]);
const handleToggle = useCallback((id) => {
setTodos((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done;
});
}, []);
const handleAdd = useCallback(() => {
setTodos((draft) => {
draft.push({
id: "todo_" + Math.random(),
title: "A new todo",
done: false
});
});
}, []);
// etc
}
```
--------------------------------
### Updating Immutable State with Immer in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/introduction.md
Illustrates updating an immutable state using Immer's `produce` function. The recipe function receives a mutable `draft` object, allowing direct modifications that Immer translates into an immutable new state. Requires importing `produce` from 'immer'.
```javascript
import {produce} from "immer"
const nextState = produce(baseState, draft => {
draft[1].done = true
draft.push({title: "Tweet about it"})
})
```
--------------------------------
### Performing Array Mutations with Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/update-patterns.md
Illustrates various array manipulation techniques (add, delete, update, insert, remove first/last, filter) using Immer's `produce`. It covers operations by index and by finding an element based on a condition, showing how standard array methods like `push`, `splice`, `pop`, `shift`, `unshift`, and `filter` can be used within the draft.
```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)
})
```
--------------------------------
### Handling State Updates with Immer Producer Returns - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript code defines a reducer function using Immer's `produce`. It demonstrates various ways a producer can handle state updates based on action types, including modifying the draft directly, returning a completely new state, and illustrating incorrect patterns like reassigning the draft or mixing draft modification with returning a new state. It shows the recommended Immer approach for mutations.
```javascript
const userReducer = produce((draft, action) => {
switch (action.type) {
case "renameUser":
// OK: we modify the current state
draft.users[action.payload.id].name = action.payload.name
return draft // same as just 'return'
case "loadUsers":
// OK: we return an entirely new state
return action.payload
case "adduser-1":
// NOT OK: This doesn't do change the draft nor return a new state!
// It doesn't modify the draft (it just redeclares it)
// In fact, this just doesn't do anything at all
draft = {users: [...draft.users, action.payload]}
return
case "adduser-2":
// NOT OK: modifying draft *and* returning a new state
draft.userCount += 1
return {users: [...draft.users, action.payload]}
case "adduser-3":
// OK: returning a new state. But, unnecessary complex and expensive
return {
userCount: draft.userCount + 1,
users: [...draft.users, action.payload]
}
case "adduser-4":
// OK: the immer way
draft.userCount += 1
draft.users.push(action.payload)
return
}
})
```
--------------------------------
### Defining a Curried Producer (Initial Approach)
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Illustrates an initial method for defining a curried producer function using Immer. It shows how Immer can infer the state type from the `draft` parameter but notes that this approach might lead to undesirable type narrowing/widening.
```typescript
// 请参阅下文以获得更好的解决方案
const toggler = produce((draft: Draft) => {
draft.done = !draft.done
})
// typeof toggler = (state: Immutable) => Writable
```
--------------------------------
### Updating Immutable State Manually in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/introduction.md
Demonstrates how to update an immutable state array by manually creating shallow copies using `slice()` and the spread operator (`...`). It shows the boilerplate required and notes the potential for accidental mutations if not careful.
```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"})
```
--------------------------------
### Producing Undefined State with Immer's nothing - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript code demonstrates how to explicitly produce the `undefined` state using Immer's built-in `nothing` token. It contrasts this with producers that return nothing or `undefined`, which default to returning the original state, highlighting the need for `nothing` to achieve an `undefined` result.
```javascript
import {produce, nothing} from "immer"
const state = {
hello: "world"
}
produce(state, draft => {})
produce(state, draft => undefined)
// Both return the original state: { hello: "world"}
produce(state, draft => nothing)
// Produces a new state, 'undefined'
```
--------------------------------
### Performing Asynchronous Updates with Immer's createDraft/finishDraft (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/async.mdx
Demonstrates how to use Immer's `createDraft` to create a mutable draft from an initial state, perform an asynchronous operation (like fetching data) based on the draft, update the draft with the result, and then use `finishDraft` to finalize the draft and produce the next immutable state. This pattern is shown as a capability but is generally discouraged in favor of the `produce` function.
```JavaScript
import {createDraft, finishDraft} from "immer"
const user = {
name: "michel",
todos: []
}
const draft = createDraft(user)
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
const loadedUser = finishDraft(draft)
```
--------------------------------
### Defining Curried Producer with Explicit Draft Type
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Demonstrates defining a curried producer by explicitly typing the draft argument using `Draft`. Explains how Immer infers the input and output types of the resulting curried function, noting the potential type narrowing/widening.
```TypeScript
// See below for a better solution!
const toggler = produce((draft: Draft) => {
draft.done = !draft.done
})
// typeof toggler = (state: Immutable) => Writable
```
--------------------------------
### Making a Class Immer Compatible (immerable)
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/complex-objects.md
Demonstrates the different ways to mark a custom JavaScript class as compatible with Immer using the `immerable` symbol. This symbol must be set on the class or its instances to allow Immer to draft them.
```js
import {immerable} from "immer"
class Foo {
[immerable] = true // 方式一
constructor() {
this[immerable] = true // 方式二
}
}
Foo[immerable] = true // 方式三
```
--------------------------------
### Correct Usage of Nested Immer Producers (JS)
Source: https://github.com/immerjs/immer/blob/main/website/docs/pitfalls.md
Demonstrates the proper pattern for using nested `produce` calls in Immer. The inner `produce` returns a new state, which must be explicitly assigned back to a property of the outer draft to reflect the changes in the final state produced by the outer `produce`.
```javascript
produce(state, draft => {
draft.user = produce(draft.user, userDraft => {
userDraft.name += "!"
})
})
```
--------------------------------
### Demonstrating Immer current and original in JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/current.md
This snippet illustrates the behavior of Immer's `produce`, `original`, and `current` functions. It modifies a draft object, logs the state of the original base object, a snapshot created by `current`, and the draft itself at different stages, including after a timeout, to show how `current` captures the state at the time of invocation.
```javascript
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(() => {
// 将在 produce 完成后执行
console.log(orig.x)
console.log(copy.x)
}, 100)
draft.x++
console.log(draft.x)
})
console.log(next.x)
// 将会打印
// 0 (orig.x)
// 1 (copy.x)
// 2 (draft.x)
// 2 (next.x)
// 0 (after timeout, orig.x)
// 1 (after timeout, copy.x)
```
--------------------------------
### Redux Reducer with Immer produce (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/example-setstate.mdx
This snippet demonstrates a standard Redux reducer wrapped with Immer's `produce` function. It shows how to handle different action types by directly mutating the `draft` state, which Immer then uses to produce the next immutable state.
```javascript
import {produce} from "immer"
// Reducer with initial state
const INITIAL_STATE = [
/* bunch of todos */
]
const todosReducer = produce((draft, action) => {
switch (action.type) {
case "toggle":
const todo = draft.find(todo => todo.id === action.id)
todo.done = !todo.done
break
case "add":
draft.push({
id: action.id,
title: "A new todo",
done: false
})
break
default:
break
}
})
```
--------------------------------
### Multiple Mutations with Immer and void - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript snippet demonstrates performing multiple draft mutations within an Immer producer using the `void` operator and the comma operator. The comma operator evaluates each expression from left to right and returns the value of the last one, which is then passed to `void` to ensure `undefined` is returned, allowing all mutations to apply.
```javascript
produce(draft => void ((draft.user.age += 1), (draft.user.height = 186)))
```
--------------------------------
### Defining Curried Producer with Explicit State Generic
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Shows the recommended way to define a curried producer when type inference is not possible, by specifying the state type using a generic (`produce`). This avoids automatic input narrowing and output widening.
```TypeScript
const toggler = produce(draft => {
draft.done = !draft.done
})
// typeof toggler = (state: Todo) => Todo
```
--------------------------------
### Marking a Class as Immerable (JavaScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/complex-objects.md
Demonstrates the three different ways to mark a JavaScript class or its instances as compatible with Immer using the `immerable` symbol. This is required for Immer to draft objects with prototypes.
```js
import {immerable} from "immer"
class Foo {
[immerable] = true // Option 1
constructor() {
this[immerable] = true // Option 2
}
}
Foo[immerable] = true // Option 3
```
--------------------------------
### Defining a Curried Producer with State Generic
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Presents the recommended approach for defining a curried producer when Immer cannot fully infer the state type. Explicitly providing the state type as a generic argument to `produce` prevents automatic input narrowing and output widening.
```typescript
const toggler = produce(draft => {
draft.done = !draft.done
})
// typeof toggler = (state: Todo) => Todo
```
--------------------------------
### Defining Curried Producer with Initial State
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Illustrates how Immer can infer the state type for a curried producer when an initial state is provided as the second argument to `produce`. No explicit type annotations are needed in this case.
```TypeScript
const state0: Todo = {
title: "test",
done: false
}
// No type annotations needed, since we can infer from state0.
const toggler = produce(draft => {
draft.done = !draft.done
}, state0)
// typeof toggler = (state: Todo) => Todo
```
--------------------------------
### Defining Curried Producer with State and Argument Generics
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Explains how to define a curried producer that accepts additional arguments. It shows how to specify both the state type and the types of the additional arguments using generics as a tuple type (`produce`).
```TypeScript
const toggler = produce((draft, newState) => {
draft.done = newState
})
// typeof toggler = (state: Todo, newState: boolean) => Todo
```
--------------------------------
### Curried Producer with Additional Parameters
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Explains how to define a curried producer that accepts parameters beyond the state. The state type and the types of additional parameters should be specified using generic arguments to `produce`, with the additional parameters defined as a tuple type.
```typescript
const toggler = produce((draft, newState) => {
draft.done = newState
})
// typeof toggler = (state: Todo, newState: boolean) => Todo
```
--------------------------------
### Using createDraft/finishDraft for Async Updates (Anti-Pattern) - Immer - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/async.mdx
This snippet demonstrates how `createDraft` and `finishDraft` can be used to initiate a draft, perform an asynchronous operation (fetching data), and then finalize the draft with the result. It is explicitly noted as an anti-pattern because updates to the original state occurring during the async process will not be reflected in the final state produced by `finishDraft`.
```javascript
import {createDraft, finishDraft} from "immer"
const user = {
name: "michel",
todos: []
}
const draft = createDraft(user)
draft.todos = await (await window.fetch("http://host/" + draft.name)).json()
const loadedUser = finishDraft(draft)
```
--------------------------------
### Curried Producer with Initial State in TypeScript
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Shows that when a curried producer is defined with an initial state provided as the second argument to `produce`, Immer can infer the state type from the initial state, making an explicit state generic unnecessary.
```typescript
const state0: Todo = {
title: "test",
done: false
}
// 不需要类型注释,因为我们可以从 state0 推断。
const toggler = produce(draft => {
draft.done = !draft.done
}, state0)
// typeof toggler = (state: Todo) => Todo
```
--------------------------------
### Using Curried Producer with React useState and Immutable Type
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Shows how to use a curried Immer producer directly within a React `useState` setter. It demonstrates type inference for the draft when the producer is passed directly and the use of the `Immutable` utility type.
```TypeScript
import {Immutable, produce} from "immer"
type Todo = Immutable<{
title: string
done: boolean
}>
// later...
const [todo, setTodo] = useState({
title: "test",
done: true
})
// later...
setTodo(
produce(draft => {
// draft will be strongly typed and mutable!
draft.done = !draft.done
})
)
```
--------------------------------
### Updating JavaScript Maps with Immer
Source: https://github.com/immerjs/immer/blob/main/website/docs/map-set.md
This snippet demonstrates how Immer's `produce` function handles updates to JavaScript `Map` objects. It shows that modifying a map within a producer results in a new map instance, even for deep changes. It also verifies that the original map remains unchanged and that the produced map is frozen, preventing external mutations.
```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"`
)
})
```
--------------------------------
### Updating React State with Immer and TypeScript
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Shows how to integrate Immer's `produce` function directly with React's `useState` hook in a TypeScript environment. It uses the `Immutable` utility type to define the state and demonstrates that the `draft` inside the producer is strongly typed and mutable.
```typescript
import {Immutable, produce} from "immer"
type Todo = Immutable<{
title: string
done: boolean
}>
// 然后...
const [todo, setTodo] = useState({
title: "test",
done: true
})
// 然后...
setTodo(
produce(draft => {
// draft 将是强类型和可变的!
draft.done = !draft.done
})
)
```
--------------------------------
### Extracting Original Object with Immer.js original
Source: https://github.com/immerjs/immer/blob/main/website/docs/original.md
Demonstrates using the `original` function within an Immer `produce` call to access the original, unproxied object corresponding to a draft. This is useful for scenarios requiring strict equality checks against the base state.
```javascript
import {original, produce} from "immer"
const baseState = {users: [{name: "Richie"}]}
const nextState = produce(baseState, draftState => {
original(draftState.users) // is === baseState.users
})
```
--------------------------------
### Single Mutation with Immer and void - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript snippet shows how to perform a single draft mutation within an Immer producer using the `void` operator. The `void` operator evaluates the expression (the mutation) and returns `undefined`, allowing the mutation to happen without accidentally returning a value that would overwrite the state.
```javascript
produce(draft => void (draft.user.age += 1))
```
--------------------------------
### Immer Producer Returning Undefined - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript snippet shows an Immer producer function that explicitly returns `undefined`. Due to JavaScript's behavior, Immer cannot distinguish this from a producer that doesn't return anything, and thus it also returns the original state by default.
```javascript
produce({}, draft => {
// Try to return undefined from the producer
return undefined
})
```
--------------------------------
### Typing Produce Output as Immutable with castImmutable (TypeScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Demonstrates how to wrap the result of an Immer `produce` call with `castImmutable` to ensure that the inferred TypeScript type of the resulting state is readonly, even when the original base state was mutable.
```TypeScript
// a mutable data structure
const baseState = {
todos: [{
done: false
}]
}
const nextState = castImmutable(produce(baseState, _draft => {}))
// inferred type of nextState is now:
{
readonly todos: ReadonlyArray<{
readonly done: boolean
}>
})
```
--------------------------------
### Using produce with Readonly State in TypeScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Illustrates how Immer's `produce` function handles `readonly` properties in TypeScript interfaces. The draft allows modification within the producer function, while the original and returned states remain readonly.
```TypeScript
import {produce} from "immer"
interface State {
readonly x: number
}
// `x` cannot be modified here
const state: State = {
x: 0
}
const newState = produce(state, draft => {
// `x` can be modified here
draft.x++
})
// `newState.x` cannot be modified here
```
--------------------------------
### Immer Producer Not Returning Value - JavaScript
Source: https://github.com/immerjs/immer/blob/main/website/docs/return.mdx
This JavaScript snippet shows an Immer producer function that does not explicitly return a value. By default, Immer interprets this as an intention to modify the draft and returns the finalized draft state.
```javascript
produce({}, draft => {
// don't do anything
})
```
--------------------------------
### Demonstrating Type Error with Readonly Array Assignment in Immer Draft (TypeScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Shows a TypeScript function using Immer's `produce` that attempts to assign a readonly array from the original state to a mutable array property on the draft, resulting in a TypeScript error due to type incompatibility.
```TypeScript
type Todo = {readonly done: boolean}
type State = {
readonly finishedTodos: readonly Todo[]
readonly unfinishedTodos: readonly Todo[]
}
function markAllFinished(state: State) {
produce(state, draft => {
draft.finishedTodos = state.unfinishedTodos
})
}
```
--------------------------------
### Basic Produce with Readonly State in TypeScript
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Demonstrates how Immer's `produce` function interacts with `readonly` properties in TypeScript. It shows that properties defined as `readonly` are immutable outside the producer but become mutable within the draft object inside the producer callback.
```ts
import {produce} from "immer"
interface State {
readonly x: number
}
// `x` 不能被修改
const state: State = {
x: 0
}
const newState = produce(state, draft => {
// `x` 可以被修改
draft.x++
})
// `newState.x` 不能在这里被修改
```
--------------------------------
### Casting Produce Result to Immutable Type
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Shows how to use the `castImmutable` utility function to explicitly type the result of a `produce` call as immutable. This is useful when the base state is mutable but you want the returned state to be treated as `readonly` by TypeScript.
```typescript
// 一个可变数据结构
const baseState = {
todos: [{
done: false
}]
}
const nextState = castImmutable(produce(baseState, _draft => {}))
// nextState 的推断类型现在是:
{
readonly todos: ReadonlyArray<{
readonly done: boolean
}>
})
```
--------------------------------
### Modifying External Data in Immer Producer (JS)
Source: https://github.com/immerjs/immer/blob/main/website/docs/pitfalls.md
Illustrates that data originating outside the Immer draft, even when added to the draft, remains the original object. Modifications to this external data within the producer will affect the original object, not a drafted copy. This highlights the importance of ensuring all data intended for mutation within the producer is part of the initial state or explicitly copied.
```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
})
}
```
--------------------------------
### Checking if a Value is a Draft with Immer.js isDraft
Source: https://github.com/immerjs/immer/blob/main/website/docs/original.md
Illustrates the use of the `isDraft` function to determine if a given value is a proxied instance created by Immer. It shows that objects and nested properties within the draft are drafts, while the final produced state is not.
```javascript
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
```
--------------------------------
### Casting Immutable Array to Mutable Draft Array
Source: https://github.com/immerjs/immer/blob/main/website/i18n/zh-CN/docusaurus-plugin-content-docs/current/typescript.mdx
Demonstrates the use of the `castDraft` utility function. It is used to inform TypeScript that a `readonly` collection from the original state should be treated as a mutable collection within the Immer draft, resolving type assignment errors.
```typescript
draft.finishedTodos = castDraft(state.unfinishedTodos)
```
--------------------------------
### Using castDraft to Resolve Readonly Array Assignment Error (TypeScript)
Source: https://github.com/immerjs/immer/blob/main/website/docs/typescript.mdx
Illustrates how to use the `castDraft` utility function within an Immer `produce` block to allow assigning a readonly array from the original state to a mutable array property on the draft, resolving a TypeScript type error.
```TypeScript
draft.finishedTodos = castDraft(state.unfinishedTodos)
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.