From ac7f04b6835d311de50dbc3a8a65cc4f795d3847 Mon Sep 17 00:00:00 2001 From: Yehonatan Sayag Date: Sun, 7 Apr 2019 10:09:43 +0300 Subject: [PATCH 1/4] Added keepState and withDataLoaded flags to makeAsyncReducer --- packages/redux-toolbelt/src/makeAsyncReducer.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/redux-toolbelt/src/makeAsyncReducer.js b/packages/redux-toolbelt/src/makeAsyncReducer.js index 62ea65b..550bd32 100644 --- a/packages/redux-toolbelt/src/makeAsyncReducer.js +++ b/packages/redux-toolbelt/src/makeAsyncReducer.js @@ -14,6 +14,8 @@ export default function makeAsyncReducer(actionCreator, options) { shouldSpread: false, shouldSetData: true, dataGetter: undefined, + keepState: false, + withDataLoaded: false, } options = Object.assign(defaults, options) @@ -25,11 +27,15 @@ export default function makeAsyncReducer(actionCreator, options) { switch (type) { case actionCreator.TYPE: return options.shouldSpread ? - { loading: true, ...(options.defaultData || {}) } : - { loading: true, [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp] } + { ...(options.keepState ? state : {}), loading: true, ...(options.defaultData || {}) } : + { + ...(options.keepState ? state : {}), + loading: true, + [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp], + } case actionCreator.success.TYPE: { if (!options.shouldSetData){ - return {loading: false} + return {loading: false, ...(options.withDataLoaded ? {dataLoaded: true} : {})} } const progress = state && state.progress === undefined ? {} : {progress: 0} const data = typeof(options.dataGetter) === 'function' ? From 7c92fb17cabdfc1cd7db5ef42b9ca346207e5053 Mon Sep 17 00:00:00 2001 From: Yehonatan Sayag Date: Sun, 7 Apr 2019 10:25:42 +0300 Subject: [PATCH 2/4] =?UTF-8?q?added=20=E2=80=98loaded=E2=80=99=20paramete?= =?UTF-8?q?r=20to=20makeAsyncReducer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/redux-toolbelt/src/makeAsyncReducer.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/redux-toolbelt/src/makeAsyncReducer.js b/packages/redux-toolbelt/src/makeAsyncReducer.js index 550bd32..3f4f122 100644 --- a/packages/redux-toolbelt/src/makeAsyncReducer.js +++ b/packages/redux-toolbelt/src/makeAsyncReducer.js @@ -14,28 +14,22 @@ export default function makeAsyncReducer(actionCreator, options) { shouldSpread: false, shouldSetData: true, dataGetter: undefined, - keepState: false, - withDataLoaded: false, } options = Object.assign(defaults, options) const defaultState = options.shouldSpread ? - { error: undefined, loading: false, ...(options.defaultData || {}) } : - { error: undefined, loading: false, [options.dataProp]: options.defaultData } + { error: undefined, loading: false, loaded: false, ...(options.defaultData || {}) } : + { error: undefined, loading: false, loaded: false, [options.dataProp]: options.defaultData } return function (state = defaultState, { type, payload, meta }) { switch (type) { case actionCreator.TYPE: return options.shouldSpread ? - { ...(options.keepState ? state : {}), loading: true, ...(options.defaultData || {}) } : - { - ...(options.keepState ? state : {}), - loading: true, - [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp], - } + { loading: true, loaded: state.loaded, ...(options.defaultData || {}) } : + { loading: true, loaded: state.loaded, [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp] } case actionCreator.success.TYPE: { if (!options.shouldSetData){ - return {loading: false, ...(options.withDataLoaded ? {dataLoaded: true} : {})} + return {loading: false, loaded: true} } const progress = state && state.progress === undefined ? {} : {progress: 0} const data = typeof(options.dataGetter) === 'function' ? From 0d70f19d5810b2db9a55801146d5f7a648c8d951 Mon Sep 17 00:00:00 2001 From: Yehonatan Sayag Date: Sun, 7 Apr 2019 11:14:20 +0300 Subject: [PATCH 3/4] =?UTF-8?q?shouldDestroyData:=20true=20resets=20the=20?= =?UTF-8?q?=E2=80=98loaded=E2=80=99=20field=20to=20false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/redux-toolbelt/README.md | 18 +++++++++++++++--- .../redux-toolbelt/src/makeAsyncReducer.js | 14 +++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/redux-toolbelt/README.md b/packages/redux-toolbelt/README.md index ed250a1..bb42a5b 100644 --- a/packages/redux-toolbelt/README.md +++ b/packages/redux-toolbelt/README.md @@ -351,6 +351,7 @@ const state = undefined asyncReducer(state, {type: '@@INIT'}) // ==> { // loading: false, +// loaded: false, // data: undefined // } ``` @@ -366,6 +367,7 @@ const state = undefined asyncReducer(state, {type: '@@INIT'}) // ==> { // loading: false, +// loaded: false, // results: [] // } ``` @@ -381,6 +383,7 @@ const state = {data: ['a']} asyncReducer(state, asyncAction.success('b')) // ==> { // loading: false, +// loaded: true, // data: ['a', 'b'] // } ``` @@ -403,6 +406,7 @@ const state = undefined asyncReducer(state, {type: '@@INIT'}) // ==> { // loading: false, +// loaded: false, // counter: 0, // status: 'offline' // } @@ -415,6 +419,7 @@ on state generically. ##### Request When the reducer gets the `request` action it updates the `loading` field. +The 'loaded' field keeps its value. ```js const asyncReducer = makeAsyncReducer(asyncAction) @@ -422,6 +427,7 @@ const state = {loading: false, data: [1, 2, 3]} asyncReducer(state, asyncAction()) // ==> { // loading: true, +// loaded: state.loaded, // data: [1, 2, 3] // } ``` @@ -437,12 +443,14 @@ const state = {loading: false, data: [1, 2, 3]} asyncReducer(state, asyncAction()) // ==> { // loading: true, +// loaded: false, // data: [] // } ``` ##### Progress When the reducer gets the `progress` action it's updating the `progress` field with the action's payload. +The 'loaded' field depends on the previous value - indicating if the data is already loaded ```js const asyncReducer = makeAsyncReducer(asyncAction) @@ -451,12 +459,13 @@ const state = {loading: true} asyncReducer(state, asyncAction.progress(5)) // ==> { // loading: true, +// loaded: state.loaded, // progress: 5 // } ``` ##### Success -When the reducer gets the `success` action is updates the `loading` to `true` and sets the `dataProp` field with the action's payload. +When the reducer gets the `success` action is updates the `loading` to `false`, the 'loaded' to 'true' and sets the `dataProp` field with the action's payload. ```js const asyncReducer = makeAsyncReducer(asyncAction) @@ -464,6 +473,7 @@ const state = {loading: true} asyncReducer(state, asyncAction.success([1, 2, 3])) // ==> { // loading: false, +// loaded: true, // 'data': [1, 2, 3] // } ``` @@ -478,7 +488,8 @@ const asyncReducer = makeAsyncReducer(asyncAction, { const state = {loading: true} asyncReducer(state, asyncAction.success([1, 2, 3])) // ==> { -// loading: false +// loading: false, +// loaded: true, // } ``` @@ -492,7 +503,8 @@ const state = {loading: true} asyncReducer(state, asyncAction.failure(`Server unreachable`)) // ==> { // loading: false, -// error: 'Server unreachable' +// error: 'Server unreachable', +// loaded: state.loaded, // } ``` diff --git a/packages/redux-toolbelt/src/makeAsyncReducer.js b/packages/redux-toolbelt/src/makeAsyncReducer.js index 3f4f122..693fea4 100644 --- a/packages/redux-toolbelt/src/makeAsyncReducer.js +++ b/packages/redux-toolbelt/src/makeAsyncReducer.js @@ -25,8 +25,16 @@ export default function makeAsyncReducer(actionCreator, options) { switch (type) { case actionCreator.TYPE: return options.shouldSpread ? - { loading: true, loaded: state.loaded, ...(options.defaultData || {}) } : - { loading: true, loaded: state.loaded, [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp] } + { + loading: true, + loaded: options.shouldDestroyData ? false : state.loaded, + ...(options.defaultData || {}) + } : + { + loading: true, + loaded: options.shouldDestroyData ? false : state.loaded, + [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp] + } case actionCreator.success.TYPE: { if (!options.shouldSetData){ return {loading: false, loaded: true} @@ -42,7 +50,7 @@ export default function makeAsyncReducer(actionCreator, options) { return {...state, progress: payload} case actionCreator.failure.TYPE: return { - ...(options.shouldDestroyDataOnError ? {} : state), + ...(options.shouldDestroyDataOnError ? {loaded: false} : state), loading: false, error: options.shouldSetError ? payload : undefined, } From d0a6f4d2b968b0b12acf1f9f11857f1337912047 Mon Sep 17 00:00:00 2001 From: Yehonatan Sayag Date: Sun, 7 Apr 2019 11:38:35 +0300 Subject: [PATCH 4/4] Fixes after tests --- packages/redux-toolbelt/src/makeAsyncReducer.js | 8 ++++---- packages/redux-toolbelt/test/makeAsyncReducer.js | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/redux-toolbelt/src/makeAsyncReducer.js b/packages/redux-toolbelt/src/makeAsyncReducer.js index 693fea4..5fcfe5f 100644 --- a/packages/redux-toolbelt/src/makeAsyncReducer.js +++ b/packages/redux-toolbelt/src/makeAsyncReducer.js @@ -28,12 +28,12 @@ export default function makeAsyncReducer(actionCreator, options) { { loading: true, loaded: options.shouldDestroyData ? false : state.loaded, - ...(options.defaultData || {}) + ...(options.defaultData || {}), } : { loading: true, loaded: options.shouldDestroyData ? false : state.loaded, - [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp] + [options.dataProp]: options.shouldDestroyData ? options.defaultData : state[options.dataProp], } case actionCreator.success.TYPE: { if (!options.shouldSetData){ @@ -43,8 +43,8 @@ export default function makeAsyncReducer(actionCreator, options) { const data = typeof(options.dataGetter) === 'function' ? options.dataGetter(state, {type, payload, meta}) : payload return options.shouldSpread ? - {loading: false, ...progress, ...data} : - {loading: false, ...progress, [options.dataProp]: data} + {loading: false, loaded: true, ...progress, ...data} : + {loading: false, loaded: true, ...progress, [options.dataProp]: data} } case actionCreator.progress.TYPE: return {...state, progress: payload} diff --git a/packages/redux-toolbelt/test/makeAsyncReducer.js b/packages/redux-toolbelt/test/makeAsyncReducer.js index 9833cae..a5b0fe4 100644 --- a/packages/redux-toolbelt/test/makeAsyncReducer.js +++ b/packages/redux-toolbelt/test/makeAsyncReducer.js @@ -16,6 +16,7 @@ test('default store', () => { expect(state).toEqual({ error: undefined, loading: false, + loaded: false, data: undefined, }) }) @@ -32,6 +33,7 @@ test('store with dataProp', () => { expect(state).toEqual({ error: undefined, loading: false, + loaded: false, someDataProp: undefined, }) @@ -39,6 +41,7 @@ test('store with dataProp', () => { state = store.getState() expect(state).toEqual({ loading: true, + loaded: false, someDataProp: undefined, }) @@ -46,6 +49,7 @@ test('store with dataProp', () => { state = store.getState() expect(state).toEqual({ loading: false, + loaded: true, someDataProp: ['some-data'], }) }) @@ -63,6 +67,7 @@ test('reducer with dataGetter', () => { expect(state).toEqual({ error: undefined, loading: false, + loaded: false, data: ['a'], }) @@ -70,6 +75,7 @@ test('reducer with dataGetter', () => { state = store.getState() expect(state).toEqual({ loading: false, + loaded: true, data: ['a', 'b'], })