diff --git a/README.md b/README.md index 22cf3b6..ab6cb68 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,36 @@ persistentStore(counter, { } } ``` +### Pausing and resuming the save to the database + +Saving the state to the database can be paused and resumed +via the `PAUSE_SAVING` and `RESUME_SAVING` actions. + +```js +import { PAUSE_SAVING, RESUME_SAVING, persistentStore, persistentReducer } + from 'redux-pouchdb-plus'; + + +// create the store the usual way... + +store.dispatch( { type: 'INCREMENT' } ); // will be saved as usual + +store.dispatch( { type: 'PAUSE_SAVING' } ); + +store.dispatch( { type: 'INCREMENT' } ); // won't be saved + +store.dispatch( { type: 'INCREMENT' } ); // won't be saved either + +store.dispatch( { type: 'RESUME_SAVING' } ); // saving is resumed + +store.dispatch( { type: 'INCREMENT' } ); // will be saved as usual +``` + +If the store changed between a `PAUSE_SAVING` and the following +`RESUME_SAVING`, the `RESUME_SAVING` will immediately trigger a save. + +Note that `PAUSE_SAVING` prevents the local store from saving its +changes to the database, but won't prevent updates coming from the database. ## Notes diff --git a/src/index.js b/src/index.js index f6c4ccb..1cb3c5c 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,9 @@ const REINIT = '@@redux-pouchdb-plus/REINIT'; const INIT = '@@redux-pouchdb-plus/INIT'; const SET_REDUCER = '@@redux-pouchdb-plus/SET_REDUCER'; +export const PAUSE_SAVING = '@@redux-pouchdb-plus/PAUSE_SAVING'; +export const RESUME_SAVING = '@@redux-pouchdb-plus/RESUME_SAVING'; + const allReducers = []; export function reinit(reducerName) { @@ -46,6 +49,7 @@ export const persistentReducer = (reducer, reducerOptions={}) => { let saveReducer; let currentState; let name = reducerOptions.name ? reducerOptions.name : reducer.name; + let paused = false; initializedReducers[name] = false; @@ -176,9 +180,37 @@ export const persistentReducer = (reducer, reducerOptions={}) => { return equalDeep(x, y); } + const reduceAndMaybeSave = (state,action) => { + const nextState = reducer(state, action); + + if (!initialState) { + initialState = nextState; + } + + const isInitialized = initializedReducers[name]; + + if (isInitialized && !paused && !isEqual(nextState, currentState)) { + currentState = nextState; + saveReducer(name, toPouch(currentState)).then(() => { + onSave(currentState); + }); + } + else if (!paused ) currentState = nextState; + + return nextState; + }; + // the proxy function that wraps the real reducer const proxyReducer = (state, action) => { switch (action.type) { + case PAUSE_SAVING: + paused = true; + return state; + + case RESUME_SAVING: + paused = false; + return reduceAndMaybeSave(state,action); + case INIT: store = action.store; storeOptions = action.storeOptions; @@ -205,24 +237,8 @@ export const persistentReducer = (reducer, reducerOptions={}) => { } // falls through - default: { - const nextState = reducer(state, action); - - if (!initialState) { - initialState = nextState; - } - - const isInitialized = initializedReducers[name]; - if (isInitialized && !isEqual(nextState, currentState)) { - currentState = nextState; - saveReducer(name, toPouch(currentState)).then(() => { - onSave(currentState); - }); - } - else currentState = nextState; - - return currentState; - } + default: + return reduceAndMaybeSave(state,action); } }; diff --git a/tests/tests.js b/tests/tests.js index 37c9f5d..34e7791 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -4,7 +4,7 @@ import PouchDB from 'pouchdb'; import timeout from 'timeout-then'; import { Map, toJS, fromJS, is } from 'immutable'; import uuid from 'uuid'; -import { persistentStore, persistentReducer, reinit, inSync } from '../src/index'; +import { PAUSE_SAVING, RESUME_SAVING, persistentStore, persistentReducer, reinit, inSync } from '../src/index'; const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; @@ -588,3 +588,49 @@ test('should allow creation of multiple redux stores with the same reducer name' //single store with the same reducer name t.throws(() => {createPersistentStore(combineReducers([thirdFinalReducer, forthFinalReducer]))}); }); + +test('should allow to pause and resume saving', t => { + t.plan(4); + + const db = new PouchDB('testdb', {db : require('memdown')}); + const createPersistentStore = persistentStore({db})(createStore); + const reducer = setupPlainReducer(); + const finalReducer = persistentReducer(reducer, {name: 'test'}); + const store = createPersistentStore(finalReducer); + + const state_x = () => store.getState().x; + const getDb = () => db.get('test').then( doc => doc.state ); + + timeout(500) + .then(() => { + store.dispatch({ type: INCREMENT }); + return timeout(500); + }) + .then(getDb) + .then( doc => { + // right now, we are in sync + t.equal( doc.x, state_x() ); + store.dispatch({ type: PAUSE_SAVING }); + store.dispatch({ type: INCREMENT }); + + return timeout(500); + }) + .then( getDb ) + .then( doc => { + // we are paused, so out of sync + t.notEqual( doc.x, state_x() ); + + store.dispatch({ type: RESUME_SAVING }); + return timeout(500); + }) + .then( getDb ) + .then( doc => { + // resuming should have kicked off a save + t.equal( doc.x, state_x() ); + store.dispatch({ type: INCREMENT }); + return timeout(500); + }) + .then( getDb ) + .then( doc => t.equal( doc.x, state_x() ) ) // still syncing + ; +});