https://github.com/zaaack/immuter
**DEPRECATED** for https://github.com/hydux/hydux-mutator
https://github.com/zaaack/immuter
deep-properties dotpath flow immutable redux-state type-safe typescript
Last synced: 11 months ago
JSON representation
**DEPRECATED** for https://github.com/hydux/hydux-mutator
- Host: GitHub
- URL: https://github.com/zaaack/immuter
- Owner: zaaack
- License: mit
- Created: 2017-05-21T02:24:43.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2017-12-19T06:45:31.000Z (over 8 years ago)
- Last Synced: 2025-03-30T00:31:36.393Z (12 months ago)
- Topics: deep-properties, dotpath, flow, immutable, redux-state, type-safe, typescript
- Language: JavaScript
- Homepage:
- Size: 713 KB
- Stars: 9
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Immuter
An immutable react/redux state update helper, easily handle nested state object with less code.
[](https://travis-ci.org/zaaack/immuter) [](https://www.npmjs.com/package/immuter) [](https://www.npmjs.com/package/immuter)
## Why
Facebook's Immutable.js is too heavy, seamless-immutable is lite and simple, and backwards-compatible with normal Arrays and Objects. But the way to update is not friendly enough for me, I have to write too much code for updating state. I also tried something like dot-prop-immutable, object-path-immutable, timm, updeep, update-immutable, etc. they all are good, but neither of them's DX is good enough for me, so I create this one based on all benefits of these.
## Install
```sh
npm i immuter
# or
yarn add immuter
```
### More Code Example:
```js
import { Struct } from 'immuter'
let struct = Struct({
title: {
zh: '哈利·波特与魔法石',
en: 'Harry Potter and the Philosopher\'s Stone',
},
author: 'J. k. rowling',
tags: ['novel', 'magic'],
})
const struct1 = struct.clone(struct => {
struct.author = 'New Author'
struct.title.en = 'New Title'
// return struct // return or not
}) // Clone struct, it will only change modified part to optimize performance.
struct.author === 'J. k. rowling' // true
struct2.author === 'New Author' // true
Struct.isStruct(struct) // true
```
## Demo
### Simple mutation method
```js
import Immuter from 'immuter'
// or import { bindObj, binComp, get, set, update, del } from 'immuter'
const book = {
title: {
zh: '哈利·波特与魔法石',
en: 'Harry Potter and the Philosopher\'s Stone',
},
author: 'J. k. rowling',
tags: ['novel', 'magic'],
}
let titleEn
let bookLite
let newBook = book
// get the English title
titleEn = Immuter.get(book, 'title.en')
// or
titleEn = Immuter.get(book, ['title', 'en'])
// return: Harry Potter and the Philosopher\'s Stone
// multiple get
bookLite = Immuter.get(book, {
'title': 'title.en',
'author': 'author',
}, {
'type': 'book',
})
// return {
// title: 'Harry Potter and the Philosopher\'s Stone',
// author: 'J. k. rowling',
// type: 'book'
// }
// set the English title
newBook = Immuter.set(newBook, 'title.zh', '新标题!')
// or
newBook = Immuter.set(newBook, ['title', 'en'], 'New title!')
// return: {
// title: {
// zh: '新标题!',
// en: 'New title!',
// },
// author: 'J. k. rowling',
// tags: ['novel', 'magic'],
// }
// set array item
newBook = Immuter.set(newBook, 'tags[0]', 'New tag')
// update array, update is almost like the set, except the value is a function to update value,
// note this function should be pure!
newBook = Immuter.update(book, 'tags', tags => tags.concat(['UK']))
// return: {
// title: {
// zh: '新标题!',
// en: 'New title!',
// },
// author: 'J. k. rowling',
// tags: ['New tag', 'magic', 'UK'],
// }
// multiple set
newBook = Immuter.set(newBook, {
'title.en': 'New Title!',
'author': 'New Author!'
})
// multiple update
newBook = Immuter.update(newBook, {
'title.en': title => title + ' (Original Edition)',
'author': author => author.toUpperCase(),
'tags': tags => tags.concat(['UK']),
})
// multiple delete
newBook = Immuter.delete(newBook, {
'title.zh': true, // this would be removed
'author': false, // this won't
'tags': false, // this won't, too
})
```
### Advance
#### bindObj
```js
import Immuter from 'immuter'
const book = {
title: {
zh: '哈利·波特与魔法石',
en: 'Harry Potter and the Philosopher\'s Stone',
},
author: 'J. k. rowling',
tags: ['novel', 'magic'],
}
let newBook = book
const immuBook = Immuter.bindObj(newBook)
const titleEn = immuBook.get('title.en')
newBook = immuBook.set('title.en', 'New title!')
immuBook.set('author', 'J.K')
newBook = immuBook.getObj()
```
#### bindComp
Using bindComp decorator to bind a React Component, with flowtype.
```js
import { Component } from 'react'
import Immuter from 'immuter'
import type { ImmuterGet, ImmuterSet, ImmuterUpdate, ImmuterDel } from 'immuter'
type State = {
title: {
zh: string,
en: string,
},
author: string,
tags: Array,
}
@Immuter.bindComp()
class CompA extends Component {
get: ImmuterGet
set: ImmuterSet
update: ImmuterUpdate
del: ImmuterDel
delete: ImmuterDel
state: State = {
title: {
zh: '哈利·波特与魔法石',
en: 'Harry Potter and the Philosopher\'s Stone',
},
author: 'J. k. rowling',
tags: ['novel', 'magic'],
}
componentDidMount() {
this.update('title.en', title => title + ' (Original Edition)')
.then((state) => {
// do what you want in setState callback
})
}
}
```
## API
### Immuter.get: (obj: T, string | Array, defaults: any) => any
Get a deep property by dot path or array path
> Note: get wouldn't deep clone result for performance issues, just make sure all your modify operations are using immuter :).
### Immuter.get(obj: T, path: { [string]: string | Array }, defaults: { [string]: any }) => { [string]: any }
Get deep properties by an Object with custom key.
### Immuter.set(obj: T, string | Array, value: any) => T
Set a deep property by dot path or array path
### Immuter.set(obj: T, pathValueMap: { [string | Array]: any }) => T
Set deep properties by an Object with Path key
### Immuter.update(obj: T, string | Array, updater: (val: any) => any) => T
Mostly like set, except passing a function to update value
### Immuter.update(obj: T, pathUpdaterMap: { [string | Array]: (val: any) => any }) => T
Multi update with an path: updater map
### Immuter.bindObj(obj: T, chain: boolean = false): ImmuterWrapper
This function will return an ImmuterWrapper instance with all functions above as it's methods, and bind the obj inside, so you don't need to pass obj.
* chain: default false, modify method would return the modified object directly, otherwise would return this for chained calls.
### Immuter.bindComp(ns: string | boolean=false, includes?: ?Array, excludes: Array = ['bindObj', 'bindComp'])
This function will bind immuter functions to React Component instance, you can get, set, delete or update component state directly with instance method `get`, `set`, `delete` or `update`.
* ns: Whether using namespace, defaults is false, means immuter functions would mount on component instance, you can call `this.get('title.en')`, `this.set('title.en', 'Some title')`, etc. in your component. Or using an special object to mount, e.g ns='immter', so you should call like this: `this.get('title.en')`, `this.set('title.en', 'Some title')`
* includes: An array of include methods, defaults is all.
* excludes: An array of exclude methods, defaults is ['bindObj', 'bindComp'].
These methods will auto update state by `this.setState`, if you need to using setState's callback feature, don't worry, all modify methods will return a promise, so you can even using async/await with it!
## Exported flow types for bindComp
```js
export type ImmuterGet = (path: GetPath, defaults: *) => *
export type ImmuterSet = (path: SetPath, value: *) => State
export type ImmuterUpdate = (path: UpdatePath, fn?: Updater) => State
export type ImmuterDel = (path: DelPath) => State
```
## Licence MIT