Skip to content

Commit

Permalink
Bug fixing, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vitsy committed Feb 9, 2018
1 parent fb07913 commit b3b820a
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 21 deletions.
5 changes: 0 additions & 5 deletions .npmignore

This file was deleted.

15 changes: 7 additions & 8 deletions src/index.js → index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function combineReducers(reducers, opts) {
}

let hasChanged = false
const nextState = {}
let nextState = {}
if (useCache && cached[type]) {
const reducerKeys = Object.keys(cached[type])
reducerKeys.forEach(key => {
Expand All @@ -47,27 +47,26 @@ export function combineReducers(reducers, opts) {
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
})
if (hasChanged) {
nextState = {...state, ...nextState}
}
} else {
useCache && (cached[type] = {})
const reducerKeys = Object.keys(reducers)
reducerKeys.forEach(key => {
const reducer = reducers[key]
const previousStateForKey = state[key]
const isReducerFunction = ~targetReducers.indexOf(key) && reducers[key] && reducers[key][method]
if (!strictMode || !method || (isReducerFunction && strictMode)) {
const actualReducer = reducers[key][isReducerFunction ? method : defaultReducerFunctionName] || defaultCommonReducer
useCache && (cached[type][key] = actualReducer)
const nextStateForKey = actualReducer(previousStateForKey, action)
const actualReducerFunc = reducers[key][isReducerFunction ? method : defaultReducerFunctionName] || defaultCommonReducer
useCache && (strictMode && isReducerFunction || !strictMode) && (cached[type][key] = actualReducerFunc)
const nextStateForKey = actualReducerFunc(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
useCache && (delete cached[type])
const errorMessage = getUndefinedStateErrorMessage(reducer, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
} else {
nextState[key] = previousStateForKey;
}
})
}
return hasChanged ? nextState : state
Expand Down
19 changes: 11 additions & 8 deletions src/index.test.js → index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {createStore} from 'redux'
import {combineReducers} from './index'

const defState= {}
const defState = {}

const reducer1 = {
copyAction: (state = defState, action) => {
Expand All @@ -21,11 +21,11 @@ const reducer2 = {
return {...state, ...action}
},
other: (state = defState, action) => {
return {}
return state
}
}

describe('test with options(defaultReducerFunctionName:other,useCache:true,strictMode:false,}', () => {
describe('test with options(defaultReducerFunctionName:other, useCache:true, strictMode:false,}', () => {
const reducer = combineReducers({
reducer1,
reducer2
Expand Down Expand Up @@ -70,16 +70,19 @@ describe('test with options(defaultReducerFunctionName:other,useCache:true,stric
expect(store.getState().reducer2.par).toEqual('both3')
})

it('should called reducer1.both only and set par ="only1"' , () => {
it('should called reducer1.both only and set par = "only1" and preserve state for reducer2' , () => {
action = {type: '[reducer1, reducer2].both', par: 'both3'}
store.dispatch(action)
let action = {type: 'reducer1.both', par: 'only1'}
store.dispatch(action)
expect(store.getState().reducer1.par).toEqual('only1')
expect(store.getState().reducer2.par).not.toEqual('only1')
expect(store.getState().reducer2.par).toBeDefined()
expect(store.getState().reducer2.par).toEqual('both3')
})

})

describe('test with options(defaultReducerFunctionName:other,useCache:true,strictMode:true,}', () => {
describe('test with options(defaultReducerFunctionName:other, useCache:true, strictMode:true,}', () => {
const reducer = combineReducers({
reducer1,
reducer2
Expand Down Expand Up @@ -108,7 +111,7 @@ describe('test with options(defaultReducerFunctionName:other,useCache:true,stric
let action = {type: 'reducer1.both', par: 'only1'}
store.dispatch(action)
expect(store.getState().reducer1.par).toEqual('only1')
expect(store.getState().reducer2.par).not.toEqual('only1')
expect(store.getState().reducer2.par).toBeDefined()
expect(store.getState().reducer2.par).toEqual('both')
})

})
116 changes: 116 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

exports.combineReducers = combineReducers;

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

var cached = {};
var options = {
defaultReducerFunctionName: 'other', //name of the reducer function which used with standart reduce using switch/case
useCache: true,
strictMode: true, //TO DO call only called reducer functions and skip other. When false need set initial state store
defaultCommonReducer: function defaultCommonReducer() {
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var action = arguments[1];
return state;
}
/**
*
* when action type looks like reducerName.methodName will be called reducer with current name and with
* function methodName()
* when action type looks like [reducerName1,reducerName2].methodName will be called reducers with
* current names with functions reducerName1.methodName(), reducerName2.methodName()
* when action type not looks like reducerName.methodName will be called defaultMethodName from current reducer
*
*
**/
};function combineReducers(reducers, opts) {
var _options$opts = _extends({}, options, opts),
defaultReducerFunctionName = _options$opts.defaultReducerFunctionName,
useCache = _options$opts.useCache,
defaultCommonReducer = _options$opts.defaultCommonReducer,
strictMode = _options$opts.strictMode;

return function () {
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var action = arguments[1];

var type = action.type,
payload = _objectWithoutProperties(action, ['type']);

var targetReducers = [];

var _type$split = type.split('.'),
_type$split2 = _slicedToArray(_type$split, 2),
reducerName = _type$split2[0],
method = _type$split2[1];

if (!options.useCache || !cached[type]) {
if (reducerName && method) {
if (/^\[[^\]]*]$/g.test(reducerName)) {
targetReducers = reducerName.substr(1).slice(0, -1).split(',').map(function (r) {
return r.trim();
});
} else {
targetReducers = [reducerName];
}
}
}

var hasChanged = false;
var nextState = {};
if (useCache && cached[type]) {
var reducerKeys = Object.keys(cached[type]);
reducerKeys.forEach(function (key) {
var actualReducer = cached[type][key];
var previousStateForKey = state[key];
var nextStateForKey = actualReducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
delete cached[type];
var errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
});
if (hasChanged) {
nextState = _extends({}, state, nextState);
}
} else {
useCache && (cached[type] = {});
var _reducerKeys = Object.keys(reducers);
_reducerKeys.forEach(function (key) {
var reducer = reducers[key];
var previousStateForKey = state[key];
var isReducerFunction = ~targetReducers.indexOf(key) && reducers[key] && reducers[key][method];
var actualReducerFunc = reducers[key][isReducerFunction ? method : defaultReducerFunctionName] || defaultCommonReducer;
useCache && (strictMode && isReducerFunction || !strictMode) && (cached[type][key] = actualReducerFunc);
var nextStateForKey = actualReducerFunc(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
useCache && delete cached[type];
var errorMessage = getUndefinedStateErrorMessage(reducer, action);
throw new Error(errorMessage);
}
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
});
}
return hasChanged ? nextState : state;
};
}

/* getUndefinedStateErrorMessage copied from redux.combineReducer */
function getUndefinedStateErrorMessage(key, action) {
var actionType = action && action.type;
var actionName = actionType && '"' + actionType.toString() + '"' || 'an action';

return 'Given action ' + actionName + ', reducer "' + key + '" returned undefined. ' + 'To ignore an action, you must explicitly return the previous state. ' + 'If you want this reducer to hold no value, you can return null instead of undefined.';
}
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "redux-reducer-functions",
"version": "0.0.1",
"description": "Use function names as action types",
"homepage": "https://github.com/vitsy/redux-reducer-functions",
"author": "Victor Tsyba <[email protected]>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/vitsy/redux-reducer-functions.git"
},
"keywords": [
"react",
"redux",
"action",
"reducer",
"constants"
],
"main": "./lib",
"files": [
"lib",
"src"
],
"scripts": {
"dev": "babel ./index.js --watch --out-dir ./lib --source-maps",
"build": "npm run clean && babel ./index.js --out-dir ./lib",
"prepublish": "npm run build",
"test": "jest --runInBand --verbose",
"clean": "rimraf ./lib"
},
"dependencies": {},
"devDependencies": {
"react": "^0.14.7",
"react-dom": "^0.14.7",
"redux": "^3.2.1",
"babel-cli": "^6.26.0",
"babel-jest": "^22.2.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"jest": "^22.2.1",
"react-test-renderer": "^16.2.0",
"rimraf": "^2.6.2"
},
"babel": {
"presets": [
"es2015",
"react",
"stage-0"
]
}
}

0 comments on commit b3b820a

Please sign in to comment.