npm.io
2.0.4 • Published 1 year ago

haaku

Licence
MIT
Version
2.0.4
Deps
1
Size
11 kB
Vulns
0
Weekly
0

haaku

Immutable state update utilities.

npm install haaku

Try on Flems.io

API

merge<T extends object>(obj: T, ...patches: Patch<T>[]): T

merge accepts two or more arguments:

  1. An object that will be used as the base; this object will remain unmutated
  2. Any number of object patches; each patch will recursively be merged with the base state

merge uses structural sharing. This means references that are not changed by patches are shared between the source and resulting object.

Example:

import { merge } from 'haaku';

const people = {
  kevin: { age: 29, pets: ['maggie'], trash: 'removeme', },
  rafael: { age: 31, pets: ['flitch', 'haku'] }
};

const updated = merge(people, {
  kevin: {
    age: 30,
    // use function patches to update a property based on its previous value
    pets: prev => [...prev, 'trixie'],
    // set a property to `undefined` to remove it from the object
    trash: undefined
  }
});

// new object is created
console.log(people === updated); // false

// original object remains unmutated
console.log(people.kevin.age); // 29
console.log(people.kevin.pets); // ['maggie']
console.log('trash' in people.kevin); // true

// unchanged references are "shared"
console.log(people.rafael === updated.rafael); // true
produce<T extends object>(obj: T, (draft: Draft<T>) => void): T

produce accepts two arguments:

  1. An object that will be used as the base; this object will remain unmutated
  2. A recipe function to produce a new object

produce is a thin wrapper over klona that provides Immer-like functionality using deep object cloning. This means produce does not use structural sharing.

Example:

import { produce } from 'haaku';

const people = {
  kevin: { age: 29, pets: ['maggie'], trash: 'removeme', },
  rafael: { age: 31, pets: ['flitch', 'haku'] }
};

const updated = produce(people, draft => {
  draft.kevin.age = 30;
  draft.kevin.pets.push('trixie');
  delete draft.kevin.trash;
});

// new object is created
console.log(people === updated); // false

// original object remains unmutated
console.log(people.kevin.age); // 29
console.log(people.kevin.pets); // ['maggie']
console.log('trash' in people.kevin); // true

// unchanged references are NOT "shared"
console.log(people.rafael === updated.rafael); // false
Caveats

merge will not deep merge nested arrays:

const a = { nums: [1, 2, 3] };
const b = merge(a, { nums: [4, 5, 6] });
console.log(b); // { nums: [4, 5, 6] }

In cases where you'd like to make changes to an existing array, you can use the spread operator in conjunction with function patches:

const a = { nums: [1, 2, 3] };
const b = merge(a, { nums: prev => [...prev, 4, 5, 6] });
console.log(b); // { nums: [1, 2, 3, 4, 5, 6] }

If you require more robust functionality for merging arrays, I recommend deepmerge and its arrayMerge utilities.

Note that function patches will be passed original references. Mutating these will mutate the original object:

const a = { nums: [1, 2, 3] };

const b = merge(a, {
  nums: prev => {
    prev.push(4); // don't do this
    return prev;
  }
});

console.log(a); // { nums: [1, 2, 3, 4] } The original object has been mutated! :(
console.log(b); // { nums: [1, 2, 3, 4] }

In cases where you'd like the benefits of mutable objects without mutating the base object, you can nest produce:

import { produce, merge } from 'haaku';

const a = { nums: [1, 2, 3] };

const b = merge(a, {
  nums: prev => produce(prev, draft => {
    draft.push(4);
  })
});

console.log(a); // { nums: [1, 2, 3] } The original object is not mutated :)
console.log(b); // { nums: [1, 2, 3, 4] }

Credits

Thank you pygy for his wisdom in fixing a major bug. Inspired by vegemite and mergerino.

Keywords