Redux Collector
Easy Collection Reducers for Redux.
http://redux-collector.mediadrake.com/
Installation
% npm install redux-collector
Usage
Redux Collector is a Reducer Generator for Collections
MyStore.js
;; { } ;
Then call actions using the types you created with collectify.
MyActions.js
const dispatch subscribe getState = store; ; ; // State is : [1, 2, 3, 4] ; // State is : [1, 2, 3, 4, 5] ;// State is : [2, 3, 4, 5, 6] ;// State is : [1, 2, 3, 3, 4] ;// State is : [3, 3, 4] ;// State is : [3]
With Reducify
The above reducers can actually be a whole lot shorter.
;; ;
This is courtesy of Reducify. Head over to their documentation and check out all the ways you can make reducers. Any argument you pass to collectify gets automatically passed to reducify.
Features
Basically, you'll get a reducer that does everything you need to arrays. When you call collectify
you'll get a chance to declare action types for any of the following methods.
Collectify
;
One primary function, wrap your item reducers with this.
itemReducer
Use reducer that you want applied to items in an array as the first (optional) argument.
actionTypes
Use this to declare the action types you want to use to manipulate your array. This is a sample configuration:
{ // reducer stuff} const actionTypes = add = 'ADD_MY_ITEM' move = 'MOVE_MY_ITEM' swap = 'SWAP_MY_ITEMS' addRange = 'ADD_MY_ITEMS' remove = 'REMOVE_MY_ITEMS' hydrate = 'HYDRATE_MY_ITEMS' sort = 'SORT_MY_ITEMS'; const myReducer = ;
If you pass only one argument, we assume the reducer is a static reducer for an empty array and will treat the one argument as the actionTypes
argument. See Reducify static reducers.
Reducers
If your Collectify reducer does not receive an action type outlined in your config, any actions you dispatch will be passed to your item reducer for every item in your array.
For example:
{ } const myStore = ; myStore; // state is [2, 4, 6] myStore; // state is [4, 6, 8]
Notice the action 'INCREMENT_BY_TWO' was applied to every item in the array.
Config
There are two configuration options you can pass in with your action types.
Collection Default
This is the default or initial value of the collection.
{ // reducer stuff} const myStore = ; myStore; // state is [1, 2]
itemDefault
The default value for any item added with add
or addRange
{ // reducer stuff} const myStore = ; myStore; // state is [10]
Actions
The actions will manipulate your array in some form or another.
Hydrate
This sets the data for your collection. It will override your entire array unless you pass in a skip
parameter.
{ // reducer stuff} const myStore = ; myStore;
Add
This will add an item to your collection. It will default to itemDefault
if you passed one with your configuration.
{ // reducer stuff} const myStore = ; myStore; // state is [10] myStore; // state is [10, 11]
You can pass any index argument as well.
{ // reducer stuff} const myStore = ; myStore; // state is [2] myStore; // state is [3, 2]
Add Range
The same as add
, except this is for adding multiple items at once.
{ // reducer stuff} const myStore = ; myStore; // state is [10] myStore; // state is [10, 11, 12]
You can pass any index argument as well.
{ // reducer stuff} const myStore = ; myStore; // state is [2, 3] myStore; // state is [4, 5, 2, 3]
Remove
This will remove items from your collection. If you do not pass a query, it will clear the entire collection.
const myStore = ; myStore; // state is [] myStore; // state is [1, 2, 3, 4] myStore; // state is [1, 2] myStore; // state is [1, 2] myStore; // state is [2]
You can pass any index argument as well.
myStore; myStore; // state is [2, 3, 4] myStore; // state is [3]
Moving and Sorting
Sort List
Using the same arguments for sort, you can modify the output of your list.
// same as aboveconst myColReducer = ; // result would be [{ord: 1, value: 3}, {ord: 2, value: 10}, {ord: 3, value: 5}]
Move
Move lets you take a set of indexes
passed to a from
argument and places them at a single indexes provided by a to
argument.
const myColReducer = ; // result would be [2, 1, 3, 4, 5] // result would be [2, 4, 1, 3, 5] // result would be [1, 3, 2, 4, 5] // result would be [2, 3, 4, 5, 1]
Swap
Swap switches item by index.
const myColReducer = ; // result would be [1, 5, 3, 4, 2]
While most swap
use cases should just take advantage of indexes
, you can swap more than one item and have everything move in a carousel.
const myColReducer = ; // result would be [1, 2, 5, 3, 4]
Check out operations for more details.
Queries
Because we may not always want to change the entire collection, Redux Collector looks for the keyword query
in your action that you dispatch. This query is processed by the collector matching method.
{ } const myStore = ; myStore; // state is [-1, 2, 4] myStore; // all items are incremented by 1// state is [-1, 3, 5]
Matcher
By default, the matcher used by query
and after
inspects your query argument and compares it against every item in the collection.
By default, your query is processed as follows:
Undefined
If undefined, we assume you want your operation applied to all items in the list.
Function
If a function, we evaluate this function passing in [state, index]
. Returning true
will say that query matches the item. Returning false
will say it doesn't.
Array
If you pass an array, we will process every item in the query with against every
(as an and, not or). For or, use $or. This is recursive.
Or
If you pass an object with the key $or
, it must have a child that is an array and we will process this against some
. This is recursive.
myStore; myStore; myStore;
Boolean
If true
, we assume you want your operation applied to all truthy items in the list.
If false
, we assume you want your operation applied to all falsy items in the list.
Object
If you pass an object, we'll pass this to the lodash function _.isMatch
with the item's state as the first argument.
Primitive
If you pass any number or string, we will do a strict equals comparison.
Custom
It's possible to customize your matcher function. Use the import {configureCollectify}
and pass in a matcher argument.
// We pass you the default matcher so you can add functionality without losing default matching behavior { if test === 'isOdd' return item % 2 === 1; return ;} const myCollectify = ; const myColReducer = ; // result is [2, 4, 4]
Operations
Beyond queries, there are other ways to apply your partial changes to your collection. These should be familiar to anyone with experience in mongo or other collection libraries.
Limit
Limits the number of operations your query will execute. This applies to all reducer behavior and remove
.
const myColReducer = ; // result would be [2, 2, 3]
const myColReducer = ; // result would be [2, 3]
Skip
Skips a number of items before executing your operation. This applies to all reducer behavior and remove
.
const myColReducer = ; // result would be [1, 3, 4]
Skip and limit can be combined together.
const myColReducer = ; // result would be [1, 3, 3]
Index
A shortcut for skip = index
and limit = 1
. Indexes are pythonic, meaning that -1 is equal to the last item in the array.
const myColReducer = ; // result would be [1, 3, 3]
const myColReducer = ; // result would be [1, 2, 4]
Indexes
The same as index
, but should be an array of indexes. Like index
, these values are pythonic.
const myColReducer = ; // result would be [2]
Range
Should be a two dimensional array of pythonic indexes of the form [start, end]
.
const myColReducer = ; // result would be [4]
After
Should be a query
that, upon first match, will determine the skip
and limit
.
const myColReducer = ; // result would be [1, 2]
Keep in mind, this is only evaluated once. If the after function returns true, the rest of the collection will have operations applied to it.
Sort
Sorts the collection before applying operations to it. This uses the same arguments available for sort
, but does not actually sort your list. This is useful when used in conjunction with any index operators.
{ } const myColReducer = ; // result would be [{ord: 1, value: 4}, {ord: 3, value: 5}, {ord: 2, value: 11}]// notice the second element was not changed
const myColReducer = ; // result would be [{ord: 1, value: 3}, {ord: 3, value: 5}]
You can pass a function to sort
if you need to do something more complicated than just checking against a single key. Keep in mind, that order
can still flip your collection. If you sort by the negative result of a key, your order
argument might undo that.
// same as aboveconst myColReducer = ; // result would be [{ord: 1, value: 3}, {ord: 3, value: 5}]
Order
This determines the direction your collections's sorting occurs in. If you do not sort your list, it can be used to just reverse the direction your operations are done in.
const myColReducer = ; // removes the last item in the list// result would be [1, 5]
const myColReducer = ; // result would be [{ord: 1, value: 3}, {ord: 3, value: 5}]
You can combine order
and sort
with a shortcut by passing an object to sort
.
// same as aboveconst myColReducer = ; // result would be [{ord: 1, value: 3}, {ord: 3, value: 5}]
Integration
Not every reducer is just a single array. Sometimes your array might be a property on the reducer itself. There's two ways to solve this.
With Pipeline
The easy way.
Redux Pipeline can you let select object properties to apply certain reducers to.
It would look something like this:
const myState = items: ; { // reducer} const myReducer = ;
Without Pipeline
The slightly more difficult, but not impossible, way.
Track all actions that would apply to your collection and run them through a collectified reducer for that item. It would look like this:
const collectified = ; { } const store = ; const dispatch getState = store;; // state is {items: [10]} ;// state is {items: [11]}
Gotchas
Multiple Aliases
A lot of operations will override behavior of other operations. Mixing them together can lead to odd consequences. We try our best to merge them all, but, when in doubt, either use specific indexes or stick to skip
and limit
.
const myColReducer = ; // result would be [1, 3, 4]// index just gets ignored
Selectors without pipeline
Redux Collector uses Reducify for your reducer config. This means you can pass any reducer function or a configuration. However, Redux Collector only works on collections, so trying to select something with your configuration will throw an error.
const myState = items: ; const myReducer = ;// throws an error, use pipeline instead
If you find yourself selecting something within collectify
, refer to the With Pipeline section of this readme.
Credits
Redux Collector is free software under the MIT license. It was created in sunny Santa Monica by Matthew Drake.