Code Monkey home page Code Monkey logo

immuter's Introduction

Immuter

An immutable react/redux state update helper, easily handle nested state object with less code.

Build Status npm npm

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

npm i immuter

# or

yarn add immuter

More Code Example:

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

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

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.

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<string>,
}

@Immuter.bindComp()
class CompA extends Component {
  get: ImmuterGet<State>
  set: ImmuterSet<State>
  update: ImmuterUpdate<State>
  del: ImmuterDel<State>
  delete: ImmuterDel<State>
  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: <T: Object>(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<T: Object>(obj: T, path: { [string]: string | Array }, defaults: { [string]: any }) => { [string]: any }

Get deep properties by an Object with custom key.

Immuter.set<T: Object>(obj: T, string | Array, value: any) => T

Set a deep property by dot path or array path

Immuter.set<T: Object>(obj: T, pathValueMap: { [string | Array]: any }) => T

Set deep properties by an Object with Path key

Immuter.update<T: Object>(obj: T, string | Array, updater: (val: any) => any) => T

Mostly like set, except passing a function to update value

Immuter.update<T: Object>(obj: T, pathUpdaterMap: { [string | Array]: (val: any) => any }) => T

Multi update with an path: updater map

Immuter.bindObj<T: Object>(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<T: Object>(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

export type ImmuterGet = (path: GetPath, defaults: *) => *
export type ImmuterSet = <State>(path: SetPath, value: *) => State
export type ImmuterUpdate = <State>(path: UpdatePath, fn?: Updater) => State
export type ImmuterDel = <State>(path: DelPath) => State

Licence MIT

immuter's People

Contributors

zaaack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

lennon-mawele

immuter's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.