diff --git a/src/index.js b/src/index.js index 15cac29..b412617 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,54 @@ -const useCombinedReducers = combinedReducers => { - // Global State - const state = Object.keys(combinedReducers).reduce( +function memoize(fn) { + let lastResult, + //initial last arguments is not going to be the same + // as anything you will pass to the function the first time + lastArguments = [{}]; + return (...currentArgs) => { + //returning memoized function + //check if currently passed arguments are the same as + // arguments passed last time + const sameArgs = + currentArgs.length === lastArguments.length && + lastArguments.reduce( + (result, lastArg, index) => + result && Object.is(lastArg, currentArgs[index]), + true, + ); + if (sameArgs) { + //current arguments are same as last so just + // return the last result and don't execute function + return lastResult; + } + //current arguments are not the same as last time + // or function called for the first time, execute the + // function and set last result + lastResult = fn.apply(null, currentArgs); + //set last args to current args + lastArguments = currentArgs; + //return result + return lastResult; + }; +} + +const createDispatch = memoize((...dispatchers) => action => + dispatchers.forEach(fn => fn(action)), +); +const createState = memoize(combinedReducers => + Object.keys(combinedReducers).reduce( (acc, key) => ({ ...acc, [key]: combinedReducers[key][0] }), {}, + ), +); +const useCombinedReducers = combinedReducers => { + // Global State + const state = createState(combinedReducers); + + const dispatchers = Object.values(combinedReducers).map( + ([, dispatch]) => dispatch, ); // Global Dispatch Function - const dispatch = action => - Object.keys(combinedReducers) - .map(key => combinedReducers[key][1]) - .forEach(fn => fn(action)); + const dispatch = createDispatch(...dispatchers); return [state, dispatch]; }; diff --git a/src/spec.js b/src/spec.js index 91d745d..dcb1779 100644 --- a/src/spec.js +++ b/src/spec.js @@ -28,3 +28,49 @@ describe('useCombinedReducer', () => { expect(bCallback.calledOnce).to.eql(true); }); }); + +describe('dispatch should not change reference', () => { + const reactDispatch = () => {}; + const otherDispatch = () => {}; + + it('should not create a new dispatch reference if not needed', () => { + const [, dispatch1] = useCombinedReducers({ + a: ['1', reactDispatch], + }); + const [, dispatch2] = useCombinedReducers({ + a: ['1', reactDispatch], + }); + expect(dispatch1).to.be.equal(dispatch2); + }); + it('should create a new dispatch reference if changed', () => { + const [, dispatch1] = useCombinedReducers({ + a: ['1', reactDispatch], + }); + const [, dispatch2] = useCombinedReducers({ + a: ['1', otherDispatch], + }); + expect(dispatch1).to.not.be.equal(dispatch2); + }); +}); + +describe('state should not change reference', () => { + const reactDispatch = () => {}; + const otherDispatch = () => {}; + const combined = { + a: ['1', reactDispatch], + }; + it('should not create a new dispatch reference if not needed', () => { + const [state1] = useCombinedReducers(combined); + const [state2] = useCombinedReducers(combined); + expect(state1).to.be.equal(state2); + }); + // it('should create a new dispatch reference if changed', () => { + // const [, dispatch1] = useCombinedReducers({ + // a: ['1', reactDispatch], + // }); + // const [, dispatch2] = useCombinedReducers({ + // a: ['1', otherDispatch], + // }); + // expect(dispatch1).to.not.be.equal(dispatch2); + // }); +});