- Notifications
You must be signed in to change notification settings - Fork4
Reducer factory functions for common data structures: counters, maps, lists (queues, stacks), sets, etc.
License
adrienjt/redux-data-structures
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Redux Data Structures is a library ofreducer makers.
Reducer makers help create common reducers like counters, maps, lists (queues, stacks), sets, etc. Most application states can be built by combining a handful of these standardized building blocks.
Redux Data Structures was developed for Redux, but does not depend on it. It can actually be used with any reactive state container, even a custom one; Redux Data Structures doesn't have any dependency.
npm install --save redux-data-structuresHere's an example from theRedux README, rewritten with Redux Data Structures:
import{createStore}from'redux';import{counter}from'redux-data-structures';constmyCounter=counter({incrementActionTypes:['INCREMENT'],decrementActionTypes:['DECREMENT'],});conststore=createStore(myCounter);store.subscribe(()=>{console.log(store.getState());});store.dispatch({type:'INCREMENT'});// 1store.dispatch({type:'INCREMENT'});// 2store.dispatch({type:'DECREMENT'});// 1
Here's a more advanced example--with the same reducer maker--of a counter from 10 to 0, decreasing as a function of the action payload, then reset, representing life points for example:
import{createStore}from'redux';import{counter}from'redux-data-structures';constlifePoints=counter({initialState:10,decrementActionTypes:['PUNCH','KICK'],decrement:action=>action.value,min:()=>0,// action => numberresetActionTypes:['INSERT_COIN'],});conststore=createStore(lifePoints);store.subscribe(()=>{console.log(store.getState());});store.dispatch({type:'PUNCH',value:5});// 5store.dispatch({type:'KICK',value:7});// 0store.dispatch({type:'INSERT_COIN'});// 10
Let's build a classic todo app with Redux Data Structures:
import{createStore,combineReducers}from'redux';import{map,set,value}from'redux-data-structures';consttodos=map({addActionTypes:['ADD_TODO'],removeActionTypes:['REMOVE_TODO'],});constcompletedTodos=set({toggleActionTypes:['TOGGLE_TODO'],removeActionTypes:['REMOVE_TODO'],keyGetter:action=>action.payload.id,});constvisibilityFilter=value({initialState:'SHOW_ALL',setActionTypes:['SET_VISIBILITY_FILTER'],valueGetter:action=>action.payload.filter,});constrootReducer=combineReducers({ todos, completedTodos, visibilityFilter,});conststore=createStore(rootReducer);
That's all for the store! We've relied heavily on the reducer makers' default options, which presume that:
- actions adhere to theFlux Standard Action (actions are plain Javascript object with a
typeandpayloadproperties), - and Todos are identified by an
idproperty, used as a key in thetodosmap (and thecompletetedTodosset).
Now let's subscribe to the store and dispatch a few actions:
store.subscribe(()=>{console.log(JSON.stringify(store.getState(),null,2));});store.dispatch({type:'ADD_TODO',payload:{id:0,text:'Go fishing',},});// {// "todos": {// "byId": {// "0": {// "id": 0,// "text": "Go fishing"// }// },// "allIds": [// 0// ]// },// "completedTodos": {},// "visibilityFilter": "SHOW_ALL"// }
Notice thattodos isnormalized, for the reasons explained in the Redux documentation.
store.dispatch({type:'TOGGLE_TODO',payload:{id:0},});// {// "todos": {// "byId": {// "0": {// "id": 0,// "text": "Go fishing"// }// },// "allIds": [// 0// ]// },// "completedTodos": {// "0": true// },// "visibilityFilter": "SHOW_ALL"// }
Compared to the original Redux Todo example, we've separated the Todo items (id, text) from their completion state. If needed, they could be combined with aselector.
store.dispatch({type:'SET_VISIBILITY_FILTER',payload:{filter:'SHOW_COMPLETED'},});// {// "todos": {// "byId": {// "0": {// "id": 0,// "text": "Go fishing"// }// },// "allIds": [// 0// ]// },// "completedTodos": {// "0": true// },// "visibilityFilter": "SHOW_COMPLETED"// }store.dispatch({type:'REMOVE_TODO',payload:{id:0},});// {// "todos": {// "byId": {},// "allIds": []// },// "completedTodos": {},// "visibilityFilter": "SHOW_COMPLETED"// }
TheREMOVE_TODO action is reduced both by thetodos map and thecompletedTodos set.
So far, the following data structures have been implemented (corresponding action types are indicated in parentheses):
- Boolean (set to true, set to false, toggle)
- Counter (increment, decrement)
- List (queue or stack: enqueue, dequeue, push, pop)
- Map (add, remove, change)
- Set (add, remove, toggle)
- Value (set)
All data structures can be reset to their initial state, and, if applicable (for lists, maps, and sets), emptied.
Each reducer maker is a higher-order function of a singleoptions object and returns a reducer:
{ ...options}=>(state,action)=>state
For each reducer maker, we describe below how theoptions object is destructured, its default property values, and how some specific properties are used.
Defaults can--and in a lot of casesshould--be overridden.
Each category of actions, e.g.,decrementActionTypes, is an array of action types (i.e., strings), so that several action types can have the same result (cf.Configuring Data Structures, above, where bothPUNCH andKICK decrementlifePoints).
{ initialState=false, trueActionTypes=[], additionalConditionToTrue=()=>true, falseActionTypes=[], additionalConditionToFalse=()=>true, toggleActionTypes=[], resetActionTypes=[],}
additionalConditionToTrue andadditionalConditionToFalse are functions ofaction and are used as such:
// ...if(trueActionTypes.includes(action.type)&&additionalConditionToTrue(action)){returntrue;}elseif(falseActionTypes.includes(action.type)&&additionalConditionToFalse(action)){returnfalse;}// ...
The default() => true is equivalent to no additional condition.
{ initialState=0, incrementActionTypes=[], increment=()=>1, max, decrementActionTypes=[], decrement=()=>1, min, resetActionTypes=[],}
increment,decrement,max, andmin are functions ofaction. Ifmax isundefined, it is not enforced. Same formin.
{ initialState=[], enqueueActionTypes=[], dequeueActionTypes=[], pushActionTypes=[], popActionTypes=[], itemGetter=action=>action.payload, resetActionTypes=[], emptyActionTypes=[],}
A list can be used as a queue or stack.enqueueActionTypes andpushActionTypes add items to the list, using theitemGetter. The defaultitemGetter adds theFlux Standard Actionpayload to the list.
{ initialState={byId:{},allIds:[],}, addActionTypes=[], changeActionTypes=[], removeActionTypes=[], keyGetter=action=>action.payload.id, itemGetter=action=>({...action.payload}), itemModifier=(item,action)=>({...item, ...action.payload}), resetActionTypes=[], emptyActionTypes=[],}
map uses thenormalized state shape recommended by Redux, as can be seen from the defaultinitialState. Warning: if you overwriteinitialState, use the same format!
The defaultkeyGetter assumes that the action payload has anid property. The defaultitemModifier overwrites the item's properties (but does not delete the ones that have disappeared in the new action payload).
{ initialState={}, addActionTypes=[], removeActionTypes=[], toggleActionTypes=[], keyGetter=action=>action.payload, resetActionTypes=[], emptyActionTypes=[],}
In Redux Data Structures, a set's state is a plain Javascript object with boolean properties, i.e. if and only ifkey is in the set,key is a property ofstate whose value istrue. Example:
{key:true}
When a key is removed from the set, the corresponding property is deleted from the state object:
{}
{ initialState=null, setActionTypes=[], valueGetter=action=>action.payload, resetActionTypes=[],}
value is the simplest data structure (to the extent that calling it a data structure is arguable).
Redux Data Structures doesn't focus on performance, but on developer productivity. In most cases, performance won't be an issue. If it is, please write an issue or submit a pull request.
The code is written in modern Javascript, transpiled with Babel, using Jest for tests. Pull requests are welcome.
Adrien Trouillaud,Codology.net
About
Reducer factory functions for common data structures: counters, maps, lists (queues, stacks), sets, etc.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.