🐬 🐬
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:
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
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