Mutative
Efficient immutable updates, 10x faster than Immer by default, even faster ...
README
Mutative
Motivation
Mutative vs Immer Performance
- ```
- Naive handcrafted reducer - No Freeze x 3,713 ops/sec ±0.86% (89 runs sampled)
- Mutative - No Freeze x 5,323 ops/sec ±1.69% (93 runs sampled)
- Immer - No Freeze x 8 ops/sec ±0.88% (23 runs sampled)
- Mutative - Freeze x 875 ops/sec ±1.20% (95 runs sampled)
- Immer - Freeze x 320 ops/sec ±0.45% (92 runs sampled)
- Mutative - Patches and No Freeze x 752 ops/sec ±0.16% (96 runs sampled)
- Immer - Patches and No Freeze x 7 ops/sec ±1.32% (23 runs sampled)
- Mutative - Patches and Freeze x 425 ops/sec ±0.33% (95 runs sampled)
- Immer - Patches and Freeze x 239 ops/sec ±0.99% (89 runs sampled)
- The fastest method is Mutative - No Freeze
- ```
OS: macOS 12.6, CPU: Apple M1 Max, Node.js: 16.14.2
Features and Benefits
Mutative Size: 4.11 kB with all dependencies, minified and gzipped.
Difference between Mutative and Immer
Mutative draft functions don't allow return value (except for void or Promise<void>), but Immer is allowed.
Installation
- ```sh
- yarn install mutative # npm install mutative
- ```
Usage
- ```ts
- import { create } from 'mutative';
- const baseState = {
- foo: 'bar',
- list: [{ text: 'coding' }],
- };
- const state = create(baseState, (draft) => {
- draft.foo = 'foobar';
- draft.list.push({ text: 'learning' });
- });
- ```
APIs
create()
- ```ts
- import { create } from 'mutative';
- const baseState = {
- foo: 'bar',
- list: [{ text: 'todo' }],
- };
- const state = create(baseState, (draft) => {
- draft.foo = 'foobar';
- draft.list.push({ text: 'learning' });
- });
- ```
create(state, fn, options) - Then options is optional.
create() - Currying
- ```ts
- const [draft, finalize] = create(baseState);
- draft.foobar.bar = 'baz';
- const state = finalize();
- ```
- ```ts
- const producer = create(() => {
- draft.foobar.bar = 'baz';
- });
- const state = producer(baseState);
- ```
They also all support set options such as const [draft, finalize] = create(baseState, { enableAutoFreeze: true });
apply()
- ```ts
- import { create, apply } from 'mutative';
- const baseState = {
- foo: 'bar',
- list: [{ text: 'todo' }],
- };
- const [state, patches, inversePatches] = create(
- baseState,
- (draft) => {
- draft.foo = 'foobar';
- draft.list.push({ text: 'learning' });
- },
- {
- enablePatches: true,
- }
- );
- const nextState = apply(baseState, patches);
- expect(nextState).toEqual(state);
- const prevState = apply(state, inversePatches);
- expect(prevState).toEqual(baseState);
- ```
current()
- ```ts
- const baseState = {
- foo: 'bar',
- list: [{ text: 'todo' }],
- };
- const state = create(baseState, (draft) => {
- draft.foo = 'foobar';
- draft.list.push({ text: 'learning' });
- expect(current(draft.list)).toEqual([{ text: 'todo' }, { text: 'learning' }]);
- });
- ```
original()
- ```ts
- const baseState = {
- foo: 'bar',
- list: [{ text: 'todo' }],
- };
- const state = create(baseState, (draft) => {
- draft.foo = 'foobar';
- draft.list.push({ text: 'learning' });
- expect(original(draft.list)).toEqual([{ text: 'todo' }]);
- });
- ```
unsafe()
- ```ts
- const baseState = {
- list: [],
- date: new Date(),
- };
- const state = create(
- baseState,
- (draft) => {
- unsafe(() => {
- draft.date.setFullYear(2000);
- });
- },
- {
- strict: true,
- }
- );
- ```
isDraft()
- ```ts
- const baseState = {
- date: new Date(),
- list: [{ text: 'todo' }],
- };
- const state = create(baseState, (draft) => {
- expect(isDraft(draft.date)).toBeFalsy();
- expect(isDraft(draft.list)).toBeTruthy();
- });
- ```
Using TypeScript
Integration with React
FAQs
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 < v0.59 or when using the Hermes engine on React Native < v0.64) does not support Proxy on Android, but React Native v0.64 with the Hermes engine support Proxy.
Migration from Immer to Mutative
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.
- ```ts
- import produce from 'immer';
- const nextState = produce(baseState, (draft) => {
- draft[1].done = true;
- draft.push({ title: 'something' });
- });
- ```
- ```ts
- import { create } from 'mutative';
- const nextState = create(baseState, (draft) => {
- draft[1].done = true;
- draft.push({ title: 'something' });
- });
- ```
- ```ts
- import { produceWithPatches, applyPatches } from 'immer';
- enablePatches();
- const baseState = {
- age: 33,
- };
- const [nextState, patches, inversePatches] = produceWithPatches(
- baseState,
- (draft) => {
- draft.age++;
- }
- );
- const state = applyPatches(nextState, inversePatches);
- expect(state).toEqual(baseState);
- ```
- ```ts
- import { create, apply } from 'mutative';
- const baseState = {
- age: 33,
- };
- const [nextState, patches, inversePatches] = create(
- baseState,
- (draft) => {
- draft.age++;
- },
- {
- enablePatches: true,
- }
- );
- const state = apply(nextState, inversePatches);
- expect(state).toEqual(baseState);
- ```