From 93d2d8ab8d547cff5fa671f4dff476401968f0d9 Mon Sep 17 00:00:00 2001 From: harm meijer Date: Wed, 30 Oct 2019 13:34:17 +0100 Subject: [PATCH 1/3] Do not create new dispatch if not needed. Dispatch has a different reference every time you call createDispatch. That is not how React.useReducer works and this should have consistant behavior. --- src/index.js | 45 +++++++++++++++++++++++++++++++++++++++++---- src/spec.js | 23 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 15cac29..1855156 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,39 @@ +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 useCombinedReducers = combinedReducers => { // Global State const state = Object.keys(combinedReducers).reduce( @@ -5,11 +41,12 @@ const useCombinedReducers = 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..0a60f60 100644 --- a/src/spec.js +++ b/src/spec.js @@ -28,3 +28,26 @@ describe('useCombinedReducer', () => { expect(bCallback.calledOnce).to.eql(true); }); }); + +describe('dispatch should not change reference', () => { + const reactDispatch = () => {}; + + 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', x => x], + }); + expect(dispatch1).to.not.be.equal(dispatch2); + }); +}); From e2bad0db8b9ffa9157438184a09769f50ac15090 Mon Sep 17 00:00:00 2001 From: harm meijer Date: Wed, 30 Oct 2019 13:55:34 +0100 Subject: [PATCH 2/3] pass git hook code coverage --- src/spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/spec.js b/src/spec.js index 0a60f60..ee7e404 100644 --- a/src/spec.js +++ b/src/spec.js @@ -31,6 +31,7 @@ describe('useCombinedReducer', () => { describe('dispatch should not change reference', () => { const reactDispatch = () => {}; + const otherDispatch = () => {}; it('should not create a new dispatch reference if not needed', () => { const [, dispatch1] = useCombinedReducers({ @@ -46,7 +47,7 @@ describe('dispatch should not change reference', () => { a: ['1', reactDispatch], }); const [, dispatch2] = useCombinedReducers({ - a: ['1', x => x], + a: ['1', otherDispatch], }); expect(dispatch1).to.not.be.equal(dispatch2); }); From 90a738009b25664683363391f749f3d06f676df7 Mon Sep 17 00:00:00 2001 From: harm meijer Date: Mon, 11 Nov 2019 20:27:34 +0100 Subject: [PATCH 3/3] Do not re reference state if not needed: See following url: https://stackoverflow.com/a/58624858/1641941 --- src/index.js | 12 +++++++----- src/spec.js | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index 1855156..b412617 100644 --- a/src/index.js +++ b/src/index.js @@ -33,13 +33,15 @@ function memoize(fn) { const createDispatch = memoize((...dispatchers) => action => dispatchers.forEach(fn => fn(action)), ); - -const useCombinedReducers = combinedReducers => { - // Global State - const state = Object.keys(combinedReducers).reduce( +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, diff --git a/src/spec.js b/src/spec.js index ee7e404..dcb1779 100644 --- a/src/spec.js +++ b/src/spec.js @@ -52,3 +52,25 @@ describe('dispatch should not change reference', () => { 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); + // }); +});