Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

immutadot is a JavaScript library to deal with nested immutable structures.

License

Notifications You must be signed in to change notification settings

zenika-open-source/immutadot

Repository files navigation

immutadot logo

A JavaScript library to deal with nested immutable structures.

set({ english: { greeting: 'Hi' } }, 'english.greeting', 'Hello')
// → { english: { greeting: 'Hello' } }

push({ i18n: { languages: ['English', 'French'] } }, 'i18n.languages', 'German', 'Spanish')
// → { i18n: { languages: ['English', 'French', 'German', 'Spanish'] } }

immutad●t gives you a short and meaningful syntax to apply operations on immutable structures.

npm version Try on RunKit Documentation

CircleCI codecov Greenkeeper

We are still writing the documentation, you can already find out about the updated API and our new package immutadot-lodash.

If you would like to try out 1.0 before its official release, install it with :

yarn add immutadot@next

or

npm install immutadot@next

Immutability

In the last few years one of our biggest challenge has been to find an efficient way to detect changes in our data to determine when to re-render our interfaces.

An immutable object is an object that cannot be changed once created. It brings several benefits1:

  • Data changes detection made simple (Shallow comparison)
  • Memoization
  • Improve rendering performances
  • Explicit data changes
  • Avoid side effects

Our approach

Concise

ES2015+ new features are great to deal with arrays and objects. As data structures expand, the code you write to make data immutable gets bigger and less readable. immutad●t uses the dot notation to address this issue.

Interoperability

immutad●t uses plain JavaScript objects so you can access your data using standard ways. Moreover, it lets you freely enjoy your favorite libraries.

Exhaustive and yet extensible

immutad●t comes with a large set of built-in utilities, mostly based on ES2015+. You can also find a package called immutadot-lodash with some of lodash's utilities. You haven't found what you're looking for? Do it yourself with the convert feature.

Learning curve

If you are already familiar with ES2015+ and lodash then you should be able to use immutad●t quickly.

Installation

immutad●t is available on npm repository.

using yarn:

$ yarn add immutadot

using npm:

$ npm install immutadot

or you can directly download sources.

Usage

ES modules:

import { set } from 'immutadot'

CommonJS:

const { set } = require('immutadot')

Example

Object used in the following example:

const animals = {
  weasels: [
    {
      vernacularName: 'badger',
      scientificName: 'Meles meles'
    },
    {
      vernacularName: 'otter',
    }
  ]
}

Let's add the otter's scientific name without mutating the original object structure.

using ES2015+:

const newAnimals = {
  ...animals,
  weasels: [...animals.weasel]
}

newAnimals.weasels[1] = {
  ...newAnimals.weasels[1],
  scientificName: 'Lutrinae'
}

using immutad●t:

const newAnimals = set(animals, 'weasels[1].scientificName', 'Lutrinae')

Feel free to try immutad●t on runkit.

Path notation

immutad●t brings a few improvements to the classic dot notation:

Slice notation

The slice notation lets you iterate over arrays to apply operations without having to map arrays at each level of imbrication.

We forgot to capitalize vernacular names in the input.

using ES2015+:

import { capitalize } from 'lodash'
const newAnimals = {
  ...animals,
  weasels: animals.weasels.map(weasel => {
    return {
      ...weasel,
      vernacularName: capitalize(weasel.vernacularName),
    }
  }),
}

using immutad●t-lodash:

import { capitalize } from 'immutadot-lodash'
const newAnimals = capitalize(animals, 'weasels[:].vernacularName')

List notation

The list notation lets you iterate over the keys of objects used as collection or map to apply operations.

toggle({ nested: { prop: { 1: { active: true }, 2: { active: false } } } }, 'nested.prop.{*}.active')
// { nested: { prop: { 1: { active: false }, 2: { active: true }] } }

toLowerCase({ nested: { prop: { 1: { msg: 'Hello' }, 2: { msg: 'Hi' }, 3: { msg: 'Good morning' } } } }, 'nested.prop{2, 3}.msg')
// { nested: { prop: { 1: { msg: 'Hello' }, 2: { msg: 'hi' }, 3: { msg: 'good morning' } } } }

Performances

We reused a simple benchmark originally made by mweststrate for immer. It updates 10.000 items out of list of 100.000 todos items, these tests were ran with Node 8.4.0 on an Intel® Core™ i7-6560U CPU @ 2.20GHz:

Update todos list
  ✓ with mutation (2ms)
  ✓ with deep cloning, then mutation (689ms)
  ✓ with ES2015 destructuring (42ms)
  ✓ with immutable (w/o conversion to plain JS objects) (50ms)
  ✓ with immutable (w/ conversion to plain JS objects) (1011ms)
  ✓ with immer (proxy implementation w/o autofreeze) (259ms)
  ✓ with immutad●t (77ms)

When applying operations on a path immutad●t tries to create the minimum of objects or arrays needed to guarantee your data structure to be immutable.

Documentation

Latest API documentation for our different packages are available here:

Looking for older versions API documentation? Links are available here.

Contributing

We want contributing to immutad●t to be fun, enjoyable, and educational for anyone, and everyone.

In the interest of fostering an open and welcoming environment, we have adopted a Code of Conduct that we expect project participants to commit to. Please read the full text so that you can understand what behavior will and will not be tolerated.

If you are interested in contributing to immutad●t, please read our contributing guide to learn more about how to suggest bugfixes and improvements.

License

immutad●t is MIT licensed.

Notes