Skip to content

Instantly share code, notes, and snippets.

@crisu83
Last active June 6, 2017 17:59
Show Gist options
  • Save crisu83/42ecffccad9d04c74605fbc75c9dc9d1 to your computer and use it in GitHub Desktop.
Save crisu83/42ecffccad9d04c74605fbc75c9dc9d1 to your computer and use it in GitHub Desktop.
General-purpose reducers for Redux.
/**
* This gist was inspired by the video course titled "Building React Applications with Idiomatic Redux"
* available on Egghead.io by the creator of Redux, Dan Abramov.
*
* The purpose of this gist is to demonstrate general purpose reducers that can be used via Redux's combineReducers
* to compose more complex reducers and therefore maximize code reuse.
*
* Feedback is more than welcome!
*
* @author Christoffer Niska <christofferniska@gmail.com>
* @link https://egghead.io/courses/building-react-applications-with-idiomatic-redux
*/
import uniq from 'lodash/uniq';
import { combineReducers } from 'redux';
/**
* Creates a reducer that manages a single value.
*
* @param {function(state, action)} shouldUpdate
* @param {function(state, action)} shouldReset
* @param {function(state, action)} getValue
* @param {*} defaultValue
* @returns {function(state, action)}
*/
const createValue = (shouldUpdate = () => true, shouldReset = () => false, getValue = (state, action) => action.payload, defaultValue = null) =>
(state = defaultValue, action) => {
if (shouldReset(state, action)) {
return defaultValue;
}
if (shouldUpdate(state, action)) {
return getValue(state, action);
}
return state;
};
/**
* Creates a reducer that manages a map.
*
* @param {function(state, action)} shouldUpdate
* @param {function(state, action)} shouldReset
* @param {function(state, action)} getValue
* @returns {function(state, action)}
*/
const createMap = (shouldUpdate = () => true, shouldReset = () => false, getValues = (state, action) => action.payload) =>
createValue(shouldUpdate, shouldReset, (state, action) => ({ ...state, ...getValues(state, action) }), {});
/**
* Creates a reducer that manages a set.
*
* @param {function(state, action)} shouldUpdate
* @param {function(state, action)} shouldReset
* @param {function(state, action)} getValue
* @returns {function(state, action)}
*/
const createSet = (shouldUpdate = () => true, shouldReset = () => false, getValues = (state, action) => action.payload) =>
createValue(shouldUpdate, shouldReset, (state, action) => uniq([...state, ...getValues(state, action)]), []);
/**
* Creates a reducer that manages a flag.
*
* @param {function(state, action)} shouldTurnOn
* @param {function(state, action)} shouldTurnOff
* @param {bool} defaultValue
* @returns {function(state, action)}
*/
const createFlag = (shouldTurnOn, shouldTurnOff, defaultValue = false) =>
(state = defaultValue, action) => {
if (shouldTurnOn(state, action)) {
return true;
}
if (shouldTurnOff(state, action)) {
return false;
}
return state;
};
/**
* Example usage.
*
* This example assumes that the data is set as a payload property on the action object.
* In the real world this might not be the case, in that case use the third parameter to customize this.
*/
const UserActions = {
FETCH_REQUEST: 'user/FETCH_REQUEST',
FETCH_SUCCESS: 'user/FETCH_SUCCESS',
FETCH_FAILURE: 'user/FETCH_FAILURE'
};
const reducer = combineReducers({
isFetching: createFlag(
(state, action) => action.type === UserActions.FETCH_REQUEST,
(state, action) => [UserActions.FETCH_SUCCESS, UserActions.FETCH_FAILURE].indexOf(action.type) !== -1
),
byId: createMap(
(state, action) => action.type === UserActions.FETCH_SUCCESS,
(state, action) => action.type === UserActions.FETCH_REQUEST
),
ids: createSet(
(state, action) => action.type === UserActions.FETCH_SUCCESS,
(state, action) => action.type === UserActions.FETCH_REQUEST
),
errorMessage: createValue(
(state, action) => action.type === UserActions.FETCH_FAILURE,
(state, action) => action.type === UserActions.FETCH_DEPARTMENT
)
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment