haaku
v2.0.4
Published
immutable state management utilities
Downloads
4
Readme
haaku
Immutable state update utilities.
npm install haakuAPI
merge<T extends object>(obj: T, ...patches: Patch<T>[]): T
merge accepts two or more arguments:
- An object that will be used as the base; this object will remain unmutated
- 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); // trueproduce<T extends object>(obj: T, (draft: Draft<T>) => void): T
produce accepts two arguments:
- An object that will be used as the base; this object will remain unmutated
- 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); // falseCaveats
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.
