{"id":13495645,"url":"https://github.com/unadlib/mutative","last_synced_at":"2025-05-13T19:06:13.509Z","repository":{"id":65232983,"uuid":"474356601","full_name":"unadlib/mutative","owner":"unadlib","description":"Efficient immutable updates, 2-6x faster than naive handcrafted reducer, and more than 10x faster than Immer.","archived":false,"fork":false,"pushed_at":"2025-05-06T17:39:40.000Z","size":16336,"stargazers_count":1729,"open_issues_count":9,"forks_count":20,"subscribers_count":13,"default_branch":"main","last_synced_at":"2025-05-06T18:43:52.592Z","etag":null,"topics":["immer","immutability","immutable","mutable","mutation","mutative","react","reducer","redux","state-management"],"latest_commit_sha":null,"homepage":"http://mutative.js.org/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unadlib.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-26T13:31:01.000Z","updated_at":"2025-05-06T17:39:50.000Z","dependencies_parsed_at":"2023-02-19T01:01:03.619Z","dependency_job_id":"bde508b5-e04b-47bc-93a1-147cc653f28f","html_url":"https://github.com/unadlib/mutative","commit_stats":{"total_commits":476,"total_committers":5,"mean_commits":95.2,"dds":"0.014705882352941124","last_synced_commit":"a6e2fdaa82660673e83ca80d2166b6838b04d439"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fmutative","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fmutative/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fmutative/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fmutative/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unadlib","download_url":"https://codeload.github.com/unadlib/mutative/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010803,"owners_count":21998993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["immer","immutability","immutable","mutable","mutation","mutative","react","reducer","redux","state-management"],"created_at":"2024-07-31T19:01:36.704Z","updated_at":"2025-05-13T19:06:13.460Z","avatar_url":"https://github.com/unadlib.png","language":"TypeScript","readme":"# Mutative\n\n\u003ca href=\"https://mutative.js.org/\" target=\"_blank\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/unadlib/mutative/main/website/static/img/ mutative.png\" height=\"120\" alt=\"Mutative Logo\" /\u003e\u003c/a\u003e\n\n![Node CI](https://github.com/unadlib/mutative/workflows/Node%20CI/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/unadlib/mutative/badge.svg?branch=main)](https://coveralls.io/github/unadlib/mutative?branch=main)\n[![npm](https://img.shields.io/npm/v/mutative.svg)](https://www.npmjs.com/package/mutative)\n[![NPM Downloads](https://img.shields.io/npm/dm/mutative)](https://npmtrends.com/mutative)\n![license](https://img.shields.io/npm/l/mutative)\n\n**Mutative** - A JavaScript library for efficient immutable updates, 2-6x faster than naive handcrafted reducer, and more than 10x faster than Immer.\n\n**Why is Mutative faster than the spread operation(naive handcrafted reducer)?**\n\nThe spread operation has performance pitfalls, which can be detailed in the following article:\n\n- \u003ca href=\"https://www.richsnapp.com/article/2019/06-09-reduce-spread-anti-pattern\" target=\"_blank\"\u003eThe reduce ({...spread}) anti-pattern\u003c/a\u003e\n- \u003ca href=\"https://jonlinnell.co.uk/articles/spread-operator-performance?fbclid=IwAR0mElQwz2aOxl8rcsqoYwkcQDlcXcwuyIsTmTAbmyzrarysS8-BC1lSY9k\" target=\"_blank\"\u003eHow slow is the Spread operator in JavaScript?\u003c/a\u003e\n\nAnd Mutative optimization focus on shallow copy optimization, more complete lazy drafts, finalization process optimization, and more.\n\n## Motivation\n\nWriting immutable updates by hand is usually difficult, prone to errors, and cumbersome. Immer helps us write simpler immutable updates with \"mutative\" logic.\n\nBut its performance issue causes a runtime performance overhead. Immer must have auto-freeze enabled by default(Performance will be worse if auto-freeze is disabled), such immutable state with Immer is not common. In scenarios such as cross-processing, remote data transfer, etc., these immutable data must be constantly frozen.\n\nThere are more parts that could be improved, such as better type inference, non-intrusive markup, support for more types of immutability, Safer immutability, [more edge cases](test/immer-non-support.test.ts), and so on.\n\nThis is why Mutative was created.\n\n## Mutative vs Naive Handcrafted Reducer Performance\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMutative vs Reducer benchmark by object:  \u003c/b\u003e\u003c/summary\u003e\n\n- Naive handcrafted reducer\n\n```ts\n// baseState type: Record\u003cstring, { value: number }\u003e\nconst state = {\n  ...baseState,\n  key0: {\n    ...baseState.key0,\n    value: i,\n  },\n};\n```\n\n- Mutative\n\n```ts\nconst state = create(baseState, (draft) =\u003e {\n  draft.key0.value = i;\n});\n```\n\n![Mutative vs Reducer benchmark by object](benchmark-object.jpg)\n\n\u003e Measure(seconds) to update the 1K-100K items object, lower is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark-object.ts)).\n\n\u003c/details\u003e\n\nMutative is up to 2x faster than naive handcrafted reducer for updating immutable objects.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMutative vs Reducer benchmark by array: \u003c/b\u003e\u003c/summary\u003e\n\n- Naive handcrafted reducer\n\n```ts\n// baseState type: { value: number }[]\n\n// slower 6x than Mutative\nconst state = [\n  { ...baseState[0], value: i },\n  ...baseState.slice(1, baseState.length),\n];\n\n// slower 2.5x than Mutative\n// const state = baseState.map((item, index) =\u003e\n//   index === 0 ? { ...item, value: i } : item\n// );\n\n// same performance as Mutative\n// const state = [...baseState];\n// state[0] = { ...baseState[0], value: i };\n```\n\n\u003e The actual difference depends on which spread operation syntax you use.\n\n- Mutative\n\n```ts\nconst state = create(baseState, (draft) =\u003e {\n  draft[0].value = i;\n});\n```\n\n![Mutative vs Reducer benchmark by array](benchmark-array.jpg)\n\n\u003e Measure(seconds) to update the 1K-100K items array, lower is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark-array.ts)).\n\n\u003c/details\u003e\n\nMutative is up to 6x faster than naive handcrafted reducer for updating immutable arrays.\n\n## Mutative vs Immer Performance\n\n\u003e Mutative passed all of Immer's test cases.\n\nMeasure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.1.0 vs Immer v10.1.1]\n\n![Benchmark](benchmark.jpg)\n\n```\nNaive handcrafted reducer - No Freeze x 4,670 ops/sec ±0.64% (96 runs sampled)\nMutative - No Freeze x 6,747 ops/sec ±0.61% (95 runs sampled)\nImmer - No Freeze x 5.65 ops/sec ±1.53% (19 runs sampled)\n\nMutative - Freeze x 1,062 ops/sec ±0.74% (95 runs sampled)\nImmer - Freeze x 394 ops/sec ±0.85% (93 runs sampled)\n\nMutative - Patches and No Freeze x 1,011 ops/sec ±0.24% (98 runs sampled)\nImmer - Patches and No Freeze x 5.64 ops/sec ±0.22% (19 runs sampled)\n\nMutative - Patches and Freeze x 545 ops/sec ±1.19% (94 runs sampled)\nImmer - Patches and Freeze x 215 ops/sec ±0.70% (86 runs sampled)\n\nThe fastest method is Mutative - No Freeze\n```\n\nRun `yarn benchmark` to measure performance.\n\n\u003e OS: macOS 14.7, CPU: Apple M1 Max, Node.js: v22.11.0\n\nImmer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.\n\nSo if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 17x performance gap between Mutative (`6,747 ops/sec`) and Immer (`394 ops/sec`).\n\nOverall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMore Performance Testing Scenarios, Mutative is up to `2.5X-82.9` faster than Immer:  \u003c/b\u003e\u003c/summary\u003e\n\n![Mutative vs Immer - All benchmark results by average multiplier](test/benchmark/results/all.jpg)\n\n\u003e [view source](https://github.com/unadlib/mutative/blob/main/test/benchmark).\n\n\u003c/details\u003e\n\n## Features and Benefits\n\n- **Mutation makes immutable updates** - Immutable data structures supporting objects, arrays, Sets and Maps.\n- **High performance** - 10x faster than immer by default, even faster than naive handcrafted reducer.\n- **Optional freezing state** - No freezing of immutable data by default.\n- **Support for JSON Patch** - Full compliance with JSON Patch specification.\n- **Custom shallow copy** - Support for more types of immutable data.\n- **Support mark for immutable and mutable data** - Allows for non-invasive marking.\n- **Safer mutable data access in strict mode** - It brings more secure immutable updates.\n- **Support for reducer** - Support reducer function and any other immutable state library.\n\n## Difference between Mutative and Immer\n\n|                                       | Mutative | Immer |\n| :------------------------------------ | -------: | :---: |\n| Custom shallow copy                   |       ✅ |  ❌   |\n| Strict mode                           |       ✅ |  ❌   |\n| No data freeze by default             |       ✅ |  ❌   |\n| Non-invasive marking                  |       ✅ |  ❌   |\n| Complete freeze data                  |       ✅ |  ❌   |\n| Non-global config                     |       ✅ |  ❌   |\n| async draft function                  |       ✅ |  ❌   |\n| Fully compatible with JSON Patch spec |       ✅ |  ❌   |\n| new Set methods(Mutative v1.1.0+)     |       ✅ |  ❌   |\n\nMutative has fewer bugs such as accidental draft escapes than Immer, [view details](https://github.com/unadlib/mutative/blob/main/test/immer-non-support.test.ts).\n\n## Installation\n\nYarn\n\n```sh\nyarn add mutative\n```\n\nNPM\n\n```sh\nnpm install mutative\n```\n\nCDN\n\n- Unpkg: `\u003cscript src=\"https://unpkg.com/mutative\"\u003e\u003c/script\u003e`\n- JSDelivr: `\u003cscript src=\"https://cdn.jsdelivr.net/npm/mutative\"\u003e\u003c/script\u003e`\n\n## Usage\n\n```ts\nimport { create } from 'mutative';\n\nconst baseState = {\n  foo: 'bar',\n  list: [{ text: 'coding' }],\n};\n\nconst state = create(baseState, (draft) =\u003e {\n  draft.list.push({ text: 'learning' });\n});\n\nexpect(state).not.toBe(baseState);\nexpect(state.list).not.toBe(baseState.list);\n```\n\n`create(baseState, (draft) =\u003e void, options?: Options): newState`\n\nThe first argument of `create()` is the base state. Mutative drafts it and passes it to the arguments of the draft function, and performs the draft mutation until the draft function finishes, then Mutative will finalize it and produce the new state.\n\nUse `create()` for more advanced features by [setting `options`](#createstate-fn-options).\n\n## APIs\n\n- [`create()`](#create)\n- [`apply()`](#apply)\n- [`current()`](#current)\n- [`original()`](#original)\n- [`unsafe()`](#unsafe)\n- [`isDraft()`](#isdraft)\n- [`isDraftable()`](#isdraftable)\n- [`rawReturn()`](#rawreturn)\n- [`makeCreator()`](#makecreator)\n- [`markSimpleObject()`](#marksimpleobject)\n\n### `create()`\n\nUse `create()` for draft mutation to get a new state, which also supports currying.\n\n```ts\nimport { create } from 'mutative';\n\nconst baseState = {\n  foo: 'bar',\n  list: [{ text: 'todo' }],\n};\n\nconst state = create(baseState, (draft) =\u003e {\n  draft.foo = 'foobar';\n  draft.list.push({ text: 'learning' });\n});\n```\n\nIn this basic example, the changes to the draft are 'mutative' within the draft callback, and `create()` is finally executed with a new immutable state.\n\n#### `create(state, fn, options)`\n\n\u003e Then options is optional.\n\n- strict - `boolean`, the default is false.\n\n  \u003e Forbid accessing non-draftable values in strict mode(unless using [unsafe()](#unsafe)).\n\n  \u003e When strict mode is enabled, mutable data can only be accessed using [`unsafe()`](#unsafe).\n\n  \u003e **It is recommended to enable `strict` in development mode and disable `strict` in production mode.** This will ensure safe explicit returns and also keep good performance in the production build. If the value that does not mix any current draft or is `undefined` is returned, then use [rawReturn()](#rawreturn).\n\n  \u003e If you'd like to enable strict mode by default in a development build and turn it off for production, you can use `strict: process.env.NODE_ENV !== 'production'`.\n\n- enablePatches - `boolean | { pathAsArray?: boolean; arrayLengthAssignment?: boolean; }`, the default is false.\n\n  \u003e Enable patch, and return the patches/inversePatches.\n\n  \u003e If you need to set the shape of the generated patch in more detail, then you can set `pathAsArray` and `arrayLengthAssignment`。`pathAsArray` default value is `true`, if it's `true`, the path will be an array, otherwise it is a string; `arrayLengthAssignment` default value is `true`, if it's `true`, the array length will be included in the patches, otherwise no include array length(**NOTE**: If `arrayLengthAssignment` is `false`, it is fully compatible with JSON Patch spec, but it may have additional performance loss), [view related discussions](https://github.com/unadlib/mutative/issues/6).\n\n- enableAutoFreeze - `boolean`, the default is false.\n\n  \u003e Enable autoFreeze, and return frozen state, and enable circular reference checking only in `development` mode.\n\n- mark - `(target) =\u003e ('mutable'|'immutable'|function) | (target) =\u003e ('mutable'|'immutable'|function)[]`\n  \u003e Set a mark to determine if the value is mutable or if an instance is an immutable, and it can also return a shallow copy function(`AutoFreeze` and `Patches` should both be disabled, Some patches operation might not be equivalent).\n  \u003e When the mark function is (target) =\u003e 'immutable', it means all the objects in the state structure are immutable. In this specific case, you can totally turn on `AutoFreeze` and `Patches`.\n  \u003e `mark` supports multiple marks, and the marks are executed in order, and the first mark that returns a value will be used.\n  \u003e When a object tree node is marked by the `mark` function as `mutable`, all of its child nodes will also not be drafted by Mutative and will retain their original values.\n\n#### `create()` - Currying\n\n- create `draft`\n\n```ts\nconst [draft, finalize] = create(baseState);\ndraft.foobar.bar = 'baz';\nconst state = finalize();\n```\n\n\u003e Support set options such as `const [draft, finalize] = create(baseState, { enableAutoFreeze: true });`\n\n- create `producer`\n\n```ts\nconst produce = create((draft) =\u003e {\n  draft.foobar.bar = 'baz';\n});\nconst state = produce(baseState);\n```\n\n\u003e Also support set options such as `const produce = create((draft) =\u003e {}, { enableAutoFreeze: true });`\n\n### `apply()`\n\nUse `apply()` for applying patches to get the new state.\n\n```ts\nimport { create, apply } from 'mutative';\n\nconst baseState = {\n  foo: 'bar',\n  list: [{ text: 'todo' }],\n};\n\nconst [state, patches, inversePatches] = create(\n  baseState,\n  (draft) =\u003e {\n    draft.foo = 'foobar';\n    draft.list.push({ text: 'learning' });\n  },\n  {\n    enablePatches: true,\n  }\n);\n\nconst nextState = apply(baseState, patches);\nexpect(nextState).toEqual(state);\nconst prevState = apply(state, inversePatches);\nexpect(prevState).toEqual(baseState);\n```\n\n### `current()`\n\nGet the current value from a draft.\n\n- For any draft where a child node has been modified, the state obtained by executing current() each time will be a new reference object.\n- For a draft where no child nodes have been modified, executing current() will always return the original state.\n\n\u003e It is recommended to minimize the number of times current() is executed when performing read-only operations, ideally executing it only once.\n\n```ts\nconst state = create({ a: { b: { c: 1 } }, d: { f: 1 } }, (draft) =\u003e {\n  draft.a.b.c = 2;\n  expect(current(draft.a)).toEqual({ b: { c: 2 } });\n  // The node `a` has been modified.\n  expect(current(draft.a) === current(draft.a)).toBeFalsy();\n  // The node `d` has not been modified.\n  expect(current(draft.d) === current(draft.d)).toBeTruthy();\n});\n```\n\n### `original()`\n\nGet the original value from a draft.\n\n```ts\nconst baseState = {\n  foo: 'bar',\n  list: [{ text: 'todo' }],\n};\n\nconst state = create(baseState, (draft) =\u003e {\n  draft.foo = 'foobar';\n  draft.list.push({ text: 'learning' });\n  expect(original(draft.list)).toEqual([{ text: 'todo' }]);\n});\n```\n\n### `unsafe()`\n\nWhen strict mode is enabled, mutable data can only be accessed using `unsafe()`.\n\n```ts\nconst baseState = {\n  list: [],\n  date: new Date(),\n};\n\nconst state = create(\n  baseState,\n  (draft) =\u003e {\n    unsafe(() =\u003e {\n      draft.date.setFullYear(2000);\n    });\n    // or return the mutable data:\n    // const date = unsafe(() =\u003e draft.date);\n  },\n  {\n    strict: true,\n  }\n);\n```\n\n\u003e If you'd like to enable strict mode by default in a development build and turn it off for production, you can use `strict: process.env.NODE_ENV !== 'production'`.\n\n### `isDraft()`\n\nCheck if a value is a draft.\n\n```ts\nconst baseState = {\n  date: new Date(),\n  list: [{ text: 'todo' }],\n};\n\nconst state = create(baseState, (draft) =\u003e {\n  expect(isDraft(draft.date)).toBeFalsy();\n  expect(isDraft(draft.list)).toBeTruthy();\n});\n```\n\n### `isDraftable()`\n\nCheck if a value is draftable\n\n```ts\nconst baseState = {\n  date: new Date(),\n  list: [{ text: 'todo' }],\n};\n\nexpect(isDraftable(baseState.date)).toBeFalsy();\nexpect(isDraftable(baseState.list)).toBeTruthy();\n```\n\n\u003e You can set a mark to determine if the value is draftable, and the mark function should be the same as passing in `create()` mark option.\n\n### `rawReturn()`\n\nFor return values that do not contain any drafts, you can use `rawReturn()` to wrap this return value to improve performance. It ensure that the return value is only returned explicitly.\n\n```ts\nconst baseState = { id: 'test' };\nconst state = create(baseState as { id: string } | undefined, (draft) =\u003e {\n  return rawReturn(undefined);\n});\nexpect(state).toBe(undefined);\n```\n\n\u003e If the return value mixes drafts, you should not use `rawReturn()`.\n\n```ts\nconst baseState = { a: 1, b: { c: 1 } };\nconst state = create(baseState, (draft) =\u003e {\n  if (draft.b.c === 1) {\n    return {\n      ...draft,\n      a: 2,\n    };\n  }\n});\nexpect(state).toEqual({ a: 2, b: { c: 1 } });\nexpect(isDraft(state.b)).toBeFalsy();\n```\n\nIf you use `rawReturn()`, we recommend that you enable `strict` mode in development.\n\n```ts\nconst baseState = { a: 1, b: { c: 1 } };\nconst state = create(\n  baseState,\n  (draft) =\u003e {\n    if (draft.b.c === 1) {\n      return rawReturn({\n        ...draft,\n        a: 2,\n      });\n    }\n  },\n  {\n    strict: true,\n  }\n);\n// it will warn `The return value contains drafts, please don't use 'rawReturn()' to wrap the return value.` in strict mode.\nexpect(state).toEqual({ a: 2, b: { c: 1 } });\nexpect(isDraft(state.b)).toBeFalsy();\n```\n\n### `makeCreator()`\n\n`makeCreator()` only takes [options](#createstate-fn-options) as the first argument, resulting in a custom `create()` function.\n\n```ts\nconst baseState = {\n  foo: {\n    bar: 'str',\n  },\n};\n\nconst create = makeCreator({\n  enablePatches: true,\n});\n\nconst [state, patches, inversePatches] = create(baseState, (draft) =\u003e {\n  draft.foo.bar = 'new str';\n});\n```\n\n### `markSimpleObject()`\n\n`markSimpleObject()` is a mark function that marks all objects as immutable.\n\n```ts\nconst baseState = {\n  foo: {\n    bar: 'str',\n  },\n  simpleObject: Object.create(null),\n};\n\nconst state = create(\n  baseState,\n  (draft) =\u003e {\n    draft.foo.bar = 'new str';\n    draft.simpleObject.a = 'a';\n  },\n  {\n    mark: markSimpleObject,\n  }\n);\n\nexpect(state.simpleObject).not.toBe(baseState.simpleObject);\n```\n\n[View more API docs](./docs/README.md).\n\n## Using TypeScript\n\n- `castDraft()`\n- `castImmutable()`\n- `castMutable()`\n- `Draft\u003cT\u003e`\n- `Immutable\u003cT\u003e`\n- `Patches`\n- `Patch`\n- `Options\u003cO, F\u003e`\n\n## Integration with React\n\n- [use-mutative](https://github.com/mutativejs/use-mutative) - A 2-6x faster alternative to useState with spread operation\n- [use-travel](https://github.com/mutativejs/use-travel) - A React hook for state time travel with undo, redo, reset and archive functionalities.\n- [zustand-mutative](https://github.com/mutativejs/zustand-mutative) - A Mutative middleware for Zustand enhances the efficiency of immutable state updates.\n\n## FAQs\n\n- I'm already using Immer, can I migrate smoothly to Mutative?\n\nYes. Unless you have to be compatible with Internet Explorer, Mutative supports almost all of Immer features, and you can easily migrate from Immer to Mutative.\n\n\u003e Migration is also not possible for React Native that does not support Proxy. React Native uses a new JS engine during refactoring - Hermes, and it (if \u003c v0.59 or when using the Hermes engine on React Native \u003c v0.64) does [not support Proxy on Android](https://github.com/facebook/hermes/issues/33), but [React Native v0.64 with the Hermes engine support Proxy](https://reactnative.dev/blog/2021/03/12/version-0.64#hermes-with-proxy-support).\n\n- Can Mutative be integrated with Redux?\n\nYes. Mutative supports return values for reducer, and `redux-toolkit` is considering support for [configurable `produce()`](https://github.com/reduxjs/redux-toolkit/pull/3074).\n\n## Migration from Immer to Mutative\n\n\u003e [mutative-compat](https://github.com/exuanbo/mutative-compat) - Mutative wrapper with full Immer API compatibility, you can use it to quickly migrate from Immer to Mutative.\n\n1. `produce()` -\u003e `create()`\n\nMutative auto freezing option is disabled by default, Immer auto freezing option is enabled by default (if disabled, Immer performance will have a more huge drop).\n\n\u003e You need to check if auto freezing has any impact on your project. If it depends on auto freezing, you can enable it yourself in Mutative.\n\n```ts\nimport produce from 'immer';\n\nconst nextState = produce(baseState, (draft) =\u003e {\n  draft[1].done = true;\n  draft.push({ title: 'something' });\n});\n```\n\nUse Mutative\n\n```ts\nimport { create } from 'mutative';\n\nconst nextState = create(baseState, (draft) =\u003e {\n  draft[1].done = true;\n  draft.push({ title: 'something' });\n});\n```\n\n2. `Patches`\n\n```ts\nimport { produceWithPatches, applyPatches } from 'immer';\n\nenablePatches();\n\nconst baseState = {\n  age: 33,\n};\n\nconst [nextState, patches, inversePatches] = produceWithPatches(\n  baseState,\n  (draft) =\u003e {\n    draft.age++;\n  }\n);\n\nconst state = applyPatches(nextState, inversePatches);\n\nexpect(state).toEqual(baseState);\n```\n\nUse Mutative\n\n```ts\nimport { create, apply } from 'mutative';\n\nconst baseState = {\n  age: 33,\n};\n\nconst [nextState, patches, inversePatches] = create(\n  baseState,\n  (draft) =\u003e {\n    draft.age++;\n  },\n  {\n    enablePatches: true,\n  }\n);\n\nconst state = apply(nextState, inversePatches);\n\nexpect(state).toEqual(baseState);\n```\n\n3. Return `undefined`\n\n```ts\nimport produce, { nothing } from 'immer';\n\nconst nextState = produce(baseState, (draft) =\u003e {\n  return nothing;\n});\n```\n\nUse Mutative\n\n```ts\nimport { create, rawReturn } from 'mutative';\n\nconst nextState = create(baseState, (draft) =\u003e {\n  return rawReturn(undefined);\n});\n```\n\n## Contributing\n\nMutative goal is to provide efficient and immutable updates. The focus is on performance improvements and providing better APIs for better development experiences. We are still working on it and welcome PRs that may help Mutative.\n\nDevelopment Workflow:\n\n- Clone Mutative repo.\n- Run `yarn install` to install all the dependencies.\n- Run `yarn prettier` to format the code.\n- `yarn test --watch` runs an interactive test watcher.\n- Run `yarn commit` to make a git commit.\n\n## License\n\nMutative is [MIT licensed](https://github.com/unadlib/mutative/blob/main/LICENSE).\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funadlib%2Fmutative","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funadlib%2Fmutative","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funadlib%2Fmutative/lists"}