### 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.