• Stars
    star
    211
  • Rank 186,867 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Immutable objects and arrays in a natural way

crio

Immutable JS objects with a natural API

Jump to the API

API Documentation

Upgrade notice

If upgrading from 4.x.x and earlier versions, please check the changelog for breaking changes.

Import

// ES2015
import crio from 'crio';

// CommonJS
const crio = require('crio').default;

// UMD
const crio = window.crio;

Usage

// you can assign with crio() directly
const crioArray = crio(['foo']);
const updatedCrioArray = crioArray.push('bar');

const crioObject = crio({foo: 'bar'});
const updatedCrioObject = crioObject.set('bar', 'baz');

// or use the convenience methods
const otherCrioArray = crio.array(['bar']);
const updatedOtherCrioArray = otherCrioArray.push('bar');

const otherCrioObject = crio.object({bar: 'baz'});
const updatedOtherCrioObject = otherCrioObject.set('bar', 'baz');

What is immutable?

When something is described as immutable, it means that it cannot change after it has been created. In JavaScript terms, this means that any attempted change to an object results in a brand new object being created, without changing the original object.

Why is this helpful?

The concept of immutability already exists in a lot of places in JavaScript, for example:

const two = 2;
const three = 3;
const five = two + three;

By adding together two and three you expect to get five, however you don't expect the value of two to change. You can continue working with it even after using it in an expression:

const two = 2;
const three = 3;
const five = two + three;
const four = two * two;

This is true of strings, numbers, undefined, and null, and is an expected behavior. The same idea, however, is not true for complex objects in JavaScript. For example:

const foo = ['foo'];
const fooBar = foo.push('bar');

The expectation is that you have pushed the value of "bar" into foo and created a new array bar that contains "foo, bar", however in reality this is what happens:

const foo = ['foo'];
const fooBar = foo.push('bar');

console.log(foo); // ['foo', 'bar']
console.log(fooBar); // 1

Basically, you have mutated foo so that it is no longer empty, and what the .push() method returns is actually the index of the item you just added. This double-standard of expectations creates a lot confusion from a development perspective, but also makes keeping track of the state of your application very difficult because there is no traceability of what transactions have occurred to create that state at any given point. This can create a lot of difficult-to-diagnose bugs and potential future regression points.

Enter crio

crio attempts to solve the problem by closing the "immutable loop" on collection items, meaning it applies immutability to objects that are normally mutable by nature by replacing mutating methods with immutable counterparts. As a point of reference:

Naturally immutable objects

  • Numbers
  • Strings
  • undefined
  • null

Naturally mutable objects

  • Arrays
  • Dates (not covered by crio)
  • Objects

The API is the same as you already know working with those objects, as well as several helpful crio-specific functions. You can work with the objects as you normally would with other libraries (lodash, for example) with the exception that any setting happens via .set() rather than direct index / property setting. There is also no change to the protoypes of native objects, so you can apply this on your existing code go-forward. Basically, you shouldn't even notice you aren't working with the native objects, save for the fact everything is immutable.

Why not just use X immutable library?

immutable is quite nice, and very highly regarded by the community, however it creates an opaque object that cannot be used with other external libraries (namely lodash) without converting back to vanilla JS. This lack of interoperability creates a lot of hoops to jump through at certain points of development, and also trains you to not necessarily think in JS, but in the library.

seamless-immutable has some great ideas, but they do not try to replace mutable methods with immutable ones, they just throw errors when you attempt them and its up to you to figure out the "right way". Also, it can be quite slow for certain operations.

Bottom line, I support each of these projects because they are trying to instill immutability in JavaScript practices, but I took a different approach that I consider the best of both worlds. :)

Browser support

  • Chrome (all versions)
  • Firefox (all versions)
  • Edge (all versions)
  • Opera 15+
  • IE 11+
  • Safari 8+
  • iOS 8+
  • Android 4+

Node support

  • 4+

Gotchas

Recursive objects are not allowed

Immutable objects with recursive values are basically impossible, and trying them will cause a stack overflow, so be mindful of that!

Development

Standard stuff, clone the repo and npm install dependencies. The npm scripts available:

  • benchmark => run benchmarks in node
  • benchmark:watch => run benchmark with persistent watcher for changes
  • build => run webpack to build crio.js with NODE_ENV=development
  • build:minifed => run webpack to build crio.min.js with NODE_ENV=production
  • compile-for-publish => run lint, test, transpile, dist
  • dev => run webpack dev server to run example app (playground!)
  • dev:production => runs dev but with NODE_ENV=production
  • dist => runs build and build-minified
  • lint => run ESLint against all files in the src folder
  • prepublish => runs compile-for-publish
  • test => run AVA test functions with NODE_ENV=test
  • test:watch => same as test, but runs persistent watcher
  • transpile => run babel against all files in src to create files in lib

More Repositories

1

fast-copy

A blazing fast deep object copier
JavaScript
1,133
star
2

moize

The consistently-fast, complete memoization solution for JS
TypeScript
892
star
3

fast-equals

A blazing fast equality comparison, either shallow or deep
TypeScript
471
star
4

unchanged

A tiny, fast, unopinionated handler for updating JS objects and arrays immutably
TypeScript
240
star
5

micro-memoize

A tiny, crazy fast memoization library for the 95% use-case
TypeScript
238
star
6

hash-it

Hash any object type based on its values
TypeScript
206
star
7

remeasure

Get position and size attributes for any React Component
JavaScript
140
star
8

inline-loops.macro

Iteration helpers that inline to native loops for performance
JavaScript
100
star
9

selectorator

Simple generator of reselect selectors
TypeScript
96
star
10

react-style-tag

Write styles declaratively in React
TypeScript
68
star
11

react-pure-lifecycle

JavaScript
65
star
12

react-windowed-list

JavaScript
61
star
13

jile

Modular CSS in pure JavaScript
JavaScript
59
star
14

react-vidz-player

HTML5 videos in a React way
JavaScript
53
star
15

fast-stringify

A blazing fast stringifier that safely handles circular objects
TypeScript
51
star
16

react-billboardjs

React component for the billboard.js charting library
JavaScript
45
star
17

curriable

Curry any function with placeholder support
TypeScript
38
star
18

vidz

A zero-dependency, framework-agnostic video implementation
JavaScript
37
star
19

switchem

An extensible, functional switch with a chainable API
JavaScript
35
star
20

waddup

A ridiculously tiny pubsub manager with no dependencies
JavaScript
27
star
21

benchee

Simple benchmarks in both node and browser
TypeScript
27
star
22

react-local-redux

Manage component-specific state as you would global state via redux
JavaScript
23
star
23

arco

JavaScript
22
star
24

flexor

JavaScript
21
star
25

qonductor

Manage your data processing with sanity
JavaScript
19
star
26

react-parm

Handle react class instances with more functional purity
JavaScript
18
star
27

react-rendered-size

Get the rendered size of a React element without needing to render it
JavaScript
17
star
28

kari

JavaScript
14
star
29

pathington

JavaScript
13
star
30

get-object-class

A more explicit improvement on typeof
JavaScript
12
star
31

redux-browser-storage

Use redux to manage localStorage and sessionStorage data
JavaScript
11
star
32

remodeled

An abstraction for the React API with functional purity
JavaScript
11
star
33

convertify

Easily convert from one object class to the next
JavaScript
10
star
34

nage

Efficient, tiny object pool
TypeScript
9
star
35

tcf

A functional try / catch / finally with async support
JavaScript
8
star
36

bolster-css

JavaScript
7
star
37

printscout

Handle print events with ease
JavaScript
7
star
38

pure-object

JavaScript
7
star
39

react-jile

JavaScript
7
star
40

highcharts-config

Declarative Highcharts configuration generator with immutable, chainable API
JavaScript
6
star
41

react-idle-manager

JavaScript
6
star
42

react-redux-partitioner

Distribute state management for more performant reactivity
TypeScript
5
star
43

repoll

JavaScript
5
star
44

isit.js

Micro check library
JavaScript
4
star
45

identitate

Custom identity functions for composability
JavaScript
4
star
46

retip

A simple react tooltip
JavaScript
3
star
47

redux-slices

Manage slices of redux store in a concise, clear way
HTML
2
star
48

utilities

A collection of utilities used across projects
JavaScript
2
star
49

doozy

Transducer library for arrays, objects, sets, and maps
JavaScript
2
star
50

what-am-i

Simple validation library
TypeScript
2
star
51

promise-polyfill

Promise polyfill with custom opt-in error handling for debug
TypeScript
1
star
52

bolster

Library to augment jQuery with additional functionality
JavaScript
1
star
53

memzee

Function memoization based on only the most recent arguments
TypeScript
1
star
54

isifier

Make your own tiny, targeted validation library
JavaScript
1
star
55

diviso

Simple, flexible state management
1
star
56

singulum

State management with sanity
JavaScript
1
star
57

planttheidea.github.io

Github IO site for planttheidea
JavaScript
1
star