haaku

0.2.2 • Public • Published

haaku

Immutable state update utilities.

npm install haaku

Try on Flems.io

Usage

Given an object:

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

We can use from or merge to produce a new object whilst leaving the original object intact. Both examples below are practically equivalent.

import { from } from 'haaku';

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

or

import { merge } from 'haaku';

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
  }
});

Both examples above will produce the given object:

// 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

// changed objects & arrays will recursively be cloned
console.log(people.kevin === updated.kevin) // false
console.log(updated.kevin.age); // 30
console.log(updated.kevin.pets); // ['maggie', 'trixie']
console.log('trash' in updated.kevin); // false

API

from(obj: Object, (draft: Proxy) => undefined): Object

from 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

merge(obj: Object, ...patches: Object[]): Object

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

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 use from in conjunction with merge:

import { from, merge } from 'haaku';

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

const b = merge(a, {
  nums: prev => from(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 clean-set, Immer, and mergerino.

Package Sidebar

Install

npm i haaku

Weekly Downloads

0

Version

0.2.2

License

MIT

Unpacked Size

10.7 kB

Total Files

5

Last publish

Collaborators

  • kevinfiol