https://github.com/andres-kovalev/general-reducer
General purpose reducer generator
https://github.com/andres-kovalev/general-reducer
general general-purpose reducer universal
Last synced: 11 days ago
JSON representation
General purpose reducer generator
- Host: GitHub
- URL: https://github.com/andres-kovalev/general-reducer
- Owner: andres-kovalev
- License: mit
- Created: 2019-11-16T22:11:42.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2020-07-13T22:39:42.000Z (over 5 years ago)
- Last Synced: 2025-08-09T06:47:24.049Z (2 months ago)
- Topics: general, general-purpose, reducer, universal
- Language: JavaScript
- Homepage: https://andres-kovalev.github.io/general-reducer/
- Size: 679 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.hbs
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://circleci.com/gh/andres-kovalev/general-reducer)
[](https://codecov.io/gh/andres-kovalev/general-reducer)
[](https://www.npmjs.com/package/general-reducer)
[](https://nodejs.org/)
[](https://www.npmjs.com/package/general-reducer)
[](https://github.com/andres-kovalev/general-reducer/blob/master/LICENSE)
[](https://www.npmjs.com/package/general-reducer)
[](https://conventionalcommits.org)# general-reducer
General purpose reducer generator
* full documentation can be found on [https://andres-kovalev.github.io/general-reducer/](https://andres-kovalev.github.io/general-reducer/)
# Description
`general-reducer` provides utilities to generate universal reducer with corresponding actions.
# Installation
As any other npm package `general-reducer` can be added to your project by following command:
```bash
npm i -S general-reducer
```# API
## Universal reducer
`general-reducer` package provides general purpose reducer to perform most common operations on store. You just need to configure your store with it.
* with react hook:
```js
import React, { useReducer } from 'react';
import { reducer } from 'general-reducer';const Component = () => {
const [ value, dispatch ] = useReducer(reducer);...
}
```* with [redux](https://redux.js.org/):
```js
import { createStore } from 'redux'
import { reducer } from 'general-reducer';const store = createStore(reducer);
```* with [react-easy-flux](https://www.npmjs.com/package/react-easy-flux):
```js
import { createStorage } from 'react-easy-flux';
import { reducer } from 'general-reducer';const {
Provider,
useStorage,
useActions
} = createStorage(reducer);
```> Since `general-reducer` uses [immutable-object-update](https://www.npmjs.com/package/immutable-object-update) under the hood, it performs immutable state update and returns frozen object.
## Built-in actions
`general-reducer` exposes `ACTIONS` map object with actions for most common operations with state:
{{#each actions}}
* [{{fn}}](src/actions/{{name}}/README.md)
{{/each}}Most of actions consumes at least 1 argument - `path` to updated element, it might be an array of items or dot-separated string. When action dispatched reducer will apply corresponding operation from [immutable-state-update](https://andres-kovalev.github.io/immutable-object-update/#api) package:
```js
import { reducer, ACTIONS } from 'general-reducer';const state = {
a: {
a1: 1,
a2: 2
},
b: {
b1: 3,
b2: 4
}
};const updated = reducer(state, ACTIONS.set('b.b1', 5));
// or
const updated = reducer(state, ACTIONS.set([ 'b', 'b1' ], 5));
```As a result we will receive new object with structure below:
```js
{
a: {
a1: 1,
a2: 2
},
b: {
b1: 3,
b2: 5
}
}
```## Action combination
Since `general-reducer` uses [](https://github.com/andres-kovalev/reducer-generator) under the hook, we're able to use [composite action](https://github.com/andres-kovalev/reducer-generator#combining-actions) to combine several simple actions and possibly improve performance a bit:
```js
const trim = (path, n) => ACTIONS.all(
ACTIONS.shift(path, n)
ACTIONS.pop(path, n)
);const updated = reducer(state, trim('a.b', 2));
```## Custom actions
It is possible to add custom actions to perform some complex updates. To so `createGeneralReducer()` function is exposed. It consumes map object with custom action update functions. Each update function will consume part of a state as an argument and should return updated value.
```js
const {
reducer,
ACTIONS
} = createGeneralReducer({
selectAll: items => items.map(
item => ({ ...item, selected: true })
),
unselectAll: items => items.map(
item => ({ ...item, selected: false })
),
removeSelected: items => items.filter(
({ selected }) => !selected
)
});
```Returned actions will consume path to state piece to be updated.
```js
const state = {
items: [
{ selected: true, text: 'Steal pants' },
{ selected: false, text: '?????' },
{ selected: false, text: 'Profit!' }
]
};const updated = reducer(state, ACTIONS.removeSelected('items'));
/*
{
items: [
{ selected: false, text: '?????' },
{ selected: false, text: 'Profit!' }
]
};
*/
```If you need any additional arguments in your update function just pass those into action creator after path:
```js
const {
reducer,
ACTIONS
} = createGeneralReducer({
add: (value, diff) => value + diff
});const state = {
a: 10
};const updated = reducer(state, ACTIONS.add('a', 5));
/*
{
a: 15
}
*/
```## Actions namespace
By default `general-reducer` uses `'general'` as a namespace for all actins, it means two different general reducers will have the same action types:
```js
const { TYPES } = createGeneralReducer({
customCase: (...) => ...
});/*
TYPES = {
insert: 'general.insert',
insertAll: 'general.insertAll',
pop: 'general.pop',
...
customCase: 'general.customCase'
}
*/
```Because of that it can be problematic to combine general reducers. To generate unique action types just provide different namespace as 2nd argument of `createGeneralReducer()` function:
```js
const { TYPES } = createGeneralReducer({
customCase: (...) => ...
}, 'customNamespace');/*
TYPES = {
insert: 'customNamespace.insert',
insertAll: 'customNamespace.insertAll',
pop: 'customNamespace.pop',
...
customCase: 'customNamespace.customCase'
}
*/
```Definitely, in most cases to extend general reducer it's better to provide custom actions or include general reducer into custom one:
```js
function customReducer(state, action) {
switch (action.type) {
...
default:
return generalReducer(state, action);
}
}
```