update-by-path
v1.1.16
Published
Easy deep immutable updates to objects
Downloads
37
Maintainers
Readme
Update by path
Intent
It is a function that allows to make immutable alterations to objects. It's meant to reduce the code needed to update a deep value without any mutations. It brings readability by reducing the verbosity.
Installation
As simple as that
npm i --save update-by-path
import update from 'update-by-path';
Real-world example
I find this package extremely useful with redux reducers, so I'm going to use one of those as an example.
First off, let's take a look at what a simple reducer would look like without this package.
const INITIAL_STATE = {
isFetching: false,
list: [],
error: null,
};
export default (state = INITIAL_STATE, action) => {
const { type, payload } = action;
switch (type) {
case types.FETCH_ATTEMPT:
return {
...state,
isFetching: true,
};
case types.FETCH_SUCCESS:
return {
...state,
isFetching: false,
list: payload,
};
default:
return state;
}
};
It can be a lot shorter if we use update from the package
...
case types.FETCH_ATTEMPT:
return update(state, { isFetching: true });
case types.FETCH_SUCCESS:
return update(state, { isFetching: false, list: payload });
default:
return state;
...
};
Same result. What about a bit more complex alterations?
Here we got to replace a list element by id:
...
case types.REPLACE_BY_ID
return {
...state,
list: state.list.map((element) => {
if (element.id === payload.id) {
return payload;
}
return element;
});
};
...
Let's apply the update function:
...
case types.REPLACE_BY_ID:
return update(state, `list[id=${payload.id}]`, payload);
...
Still a single line, and still completely immutable.
API
It accepts either 2, or 3 arguments.
update(object, 'deep.path.to.prop', valueToInsert);
update(object, {
'deep.path.to.props': valueToInsert,
'another.path': valueToInsert,
});
They are completely identical, though the second option allows to make multiple insertions at a time.
Create new nodes
If the path contains nodes that do not exist in the source object, they are going to be created
update({}, 'deep.path', 'value');
// { deep: { path: 'value' } }
Use function as a value
If you need to generate next value based on the previous one, use a function
const user = {
name: 'John',
};
update(user, {
name: name => name.toUpperCase(),
});
// { name: 'JOHN' }
Go deep inside arrays
By value
const user = {
skills: ['html', 'javascript'],
};
update(user, `skills[javascript]`, 'JavaScript');
// { skills: ['html', 'JavaScript' ] }
By index
const user = {
skills: ['html', 'javascript'],
};
update(user, `skills[0]`, skill => skill.toUpperCase());
// { skills: ['HTML', 'javascript' ] }
By property value
const user = {
skills: [{ name: 'html'}, { name: 'javascript' }],
};
update(user, `skills[name=javascript]`, { name: 'JavaScript' });
// { skills: [{ name: 'html'}, { name: 'JavaScript' }] },
If there are no matches for your query the source object remains intact.
Go even deeper inside array elements.
Alter a property of the matching array element. It doesn't metter which kind of query to use to match the element.
const user = {
skills: [{ name: 'html'}, { name: 'javascript' }],
};
update(user, `skills[name=html].name`, name => name.toUpperCase());
// { skills: [{ name: 'HTML'}, { name: 'javascript' }] },
Contribute
First off, thanks for taking the time to contribute! Now, take a moment to be sure your contributions make sense to everyone else.
LICENSE
This project is licensed under the MIT License - see the LICENSE file for details.