Giter VIP home page Giter VIP logo

js-immutable's Introduction

Motivation ๐Ÿฌ๐Ÿฌ

Consider the scenario where you have to set a new value to "temporary" field without mutating original state.

const state = {
  detail: {
    age: 30,
    friends: ['Roshan'],
    personal: {
      address: {
        permanent: 'Kathmandu',
        temporary: 'Pokhara'
      },
      spouse: 'Nancy'
    }
  }
}          

Javascript way of setting a new value without modifying original state would be something like this:

// For my brain, this is too much to wrap around just to change a single field.
// There must be some better way. 
const newState = {
  ...state,
  detail: {
    ...state.detail,
    personal: {
      ...state.detail.personal,
      address: {
        ...state.detail.personal.address,
        temporary: 'New Random Location' // here is the actual change
      }
    }
  }
} 

Look at all the repetition! This is not only annoying, but also provides a large surface area for bugs.

Problems with the above code:

๐Ÿ“Œ Need to keep track of whole state tree just to perform such small modification.

๐Ÿ“Œ Need to make sure that state tree is not mutated while returning new state.

๐Ÿ“Œ Need to make sure structure of state tree is not changed while returning new state. Specially it becomes nightmare in real world application where you don't know which action modified the entire redux state.

๐Ÿ“Œ If structure of original state tree is modified, then every action reducer must be re-written. i.e Your reducer has an dependency on structure of redux state.

JS Immutable in Action

// Add as a dependency
npm install js-immutable --save
import reduce from 'js-immutable';
// create a address reducer by passing a selector

const addressReducer = reduce({
  detail: {
    personal: {
      address: {
        temporary: '#',
      }
    }
  }
})
// No mutation fear
// State Structure is maintained
// No dependency to the state structure while returning new state

const newState = addressReducer(state)
  .set('New Random Location')
  .apply();

A more complex scenario where we need to append new friend to the friends list and set new value to permanent address.

const complexReducer = reduce({
  detail: {
    friends: '#friends', // selector
    personal: {
      address: {
        permanent: '#permanent' // selector 
      }
    }
  }
});

// Clean and elegant 
const newState = complexReducer(state)
  .of('#friends') // using friends selector and appending
  .append('John')
  .of('#permanent') // using permanent selector and setting
  .set('New Value')
  .apply();

// or you can simply pipe it through predicate

const newState = complexReducer(state)
	.of('#friends')
	.pipe(friends => friends.concat('John'))
	.of('#permanent')
	.pipe(value => value.toUpperCase())
	.apply();

Note

'#' is the default selector. You don't need to use "of("some selector")' when you use '#' as a selector.

Benefits

๐Ÿ“Œ Structural Sharing out of Box. Performant! ๐Ÿ“Œ Your code is independent of the state tree and it's structure.

๐Ÿ“Œ You don't have to worry about mutation. Js-Immutable handles it for you.

๐Ÿ“Œ You only have to make changes to selector if structure of redux state tree is modified. Your reducer will never be touched in the case of state tree modification.

๐Ÿ“Œ It looks functional, clean, simple and easy to follow. It just makes life of your co-worker easier.

More on JS-Immutable

Selector

Selector are plain object that helps to select the fields on the state tree. Default selector value is '#'. The selector value must start with '#'. If your selector has multiple fields to select, Make sure they start with '#' and are unique.

// Example of Selector

const selector = {
  person: {
    friends: '#' // default
  }
}
// Example of using the above selector

const friendsReducer = reduce(selector);

const newState = friendsReducer(state)
  .append('My new friend') // no need of "of('#')" since it is the default one.
  .apply();
// Example of multiple selector

const nextSelector = {
  name: '#name', // named selector (unique)
  detail: {
    address: '#address' // named selector (unique)
  }
}

// Example of using the above selector

const multipleReducer = reduce(nextSelector);

const newState = multipleReducer(state)
	.of('#name')
	.set('New Name')
	.of('#address')
	.merge({temporary: 'Pokhara'})
	.apply();

Available Methods

๐Ÿ’ง set(value: any)

Sets the new value to the target field.

๐Ÿ’ง append(value: any)

Appends new value on the target array.

๐Ÿ’ง merge({key: value})

Merge object on the target object.

๐Ÿ’ง extend([]: any)

Concatenate array on the target array

๐Ÿ’ง delete(key: any)

Delete a key on the target object or target array.

๐Ÿ’ง pipe(predicate: function)

Applies a function/predicate to the target value.

Utility Method

๐Ÿ’ง Of(selectorName: any)

Helps to select the specific target so that it apply transformation to that target in the object.

// if we have a multiple targets in a single selector
const selector = {
  task: {
    done: '#done',
    taskDetail: '#taskDetail'
  }
}
const taskReducer = reduce(selector)

const newState = taskReducer
	.of('#done')
	.set(true)
	.of('#taskDetail')
	.set('some new Detail')
	.apply();

Note:

If you think I have missed methods that is crucially important, then please send a Pull Request.

License

Copyright ยฉ 2015-2016 Robus, LLC. This source code is licensed under the MIT license found in the [LICENSE.txt] The documentation to the project is licensed under the CC BY-SA 4.0 license.


Made with โ™ฅ by Robus Gauli ([@robusgauli]

js-immutable's People

Contributors

frenzzy avatar greenkeeperio-bot avatar koistya avatar lguzzon avatar robusgauli avatar sbimochan avatar xdamman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

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.