UNPKG

redux-persist

Version:

persist and rehydrate redux stores

211 lines (192 loc) 5.18 kB
'use strict'; var forEach = require('lodash.foreach'); var constants = require('./constants'); module.exports = function persistStore(store, config, cb) { //defaults config = config || {}; var blacklist = config.blacklist || []; var rehydrateAction = config.rehydrateAction || defaultRehydrateAction; var completeAction = config.completeAction || defaultCompleteAction; var serialize = config.serialize || defaultSerialize; var deserialize = config.deserialize || defaultDeserialize; var transforms = config.transforms || []; var storage = config.storage || defaultStorage; //initialize values var timeIterator = null; var lastState = store.getState(); //rehydrate var restoreCount = 0; var completionCount = 0; if (Object.keys(lastState).length === 0) { store.dispatch(completeAction()); } forEach(lastState, function (s, key) { if (blacklist.indexOf(key) !== -1) { return; } restoreCount += 1; setImmediate(function () { rehydrate(key, function () { completionCount += 1; if (completionCount === restoreCount) { store.dispatch(completeAction()); cb && cb(); } }); }); }); var storesToProcess = []; //store state to disk var unsub = store.subscribe(function () { //Clear unfinished timeIterator if exists if (timeIterator !== null) { clearInterval(timeIterator); } var state = store.getState(); forEach(state, function (subState, key) { if (blacklist.indexOf(key) !== -1) { return; } //only store keys that have changed if (lastState[key] === state[key]) { return; } if (storesToProcess.indexOf(key) !== -1) { return; } storesToProcess.push(key); }); //time iterator runs every 33ms (30fps) timeIterator = setInterval(function () { if (storesToProcess.length === 0) { clearInterval(timeIterator); return; } var key = createStorageKey(storesToProcess[0]); var endState = transforms.reduce(function (subState, transformer) { return transformer['in'](subState); }, state[storesToProcess[0]]); if (typeof endState !== 'undefined') { var serial = serialize(endState); storage.setItem(key, serial, warnIfSetError(key)); } storesToProcess.shift(); }, 33); lastState = state; }); var purgeMode = false; function rehydrate(key, cb) { storage.getItem(createStorageKey(key), function (err, serialized) { var state = null; try { if (err) { throw err; } var data = deserialize(serialized); state = transforms.reduceRight(function (subState, transformer) { return transformer.out(subState); }, data); } catch (e) { console.warn('Error restoring data for key:', key, e); storage.removeItem(key, warnIfRemoveError(key)); } if (state !== null) { if (purgeMode === '*' || Array.isArray(purgeMode) && purgeMode.indexOf(key) !== -1) { return; } store.dispatch(rehydrateAction(key, state)); } cb(); }); } return { purge: function purge(keys) { purgeMode = keys; forEach(keys, function (key) { storage.removeItem(createStorageKey(key), warnIfRemoveError(key)); }); }, purgeAll: function purgeAll() { purgeMode = '*'; storage.getAllKeys(function (err, keys) { forEach(keys, function (key) { if (key.indexOf(constants.keyPrefix) === 0) { storage.removeItem(key, warnIfRemoveError(key)); } }); }); } }; }; function warnIfRemoveError(key) { return function removeError(err) { if (err) { console.warn('Error storing data for key:', key, err); } }; } function warnIfSetError(key) { return function setError(err) { if (err) { console.warn('Error storing data for key:', key, err); } }; } function createStorageKey(key) { return constants.keyPrefix + key; } function defaultRehydrateAction(key, data) { return { type: constants.REHYDRATE, key: key, payload: data }; } function defaultCompleteAction() { return { type: constants.REHYDRATE_COMPLETE }; } function defaultSerialize(data) { return JSON.stringify(data); } function defaultDeserialize(serial) { return JSON.parse(serial); } var defaultStorage = { getItem: function getItem(key, cb) { try { var s = localStorage.getItem(key); cb(null, s); } catch (e) { cb(e); } }, setItem: function setItem(key, string, cb) { try { localStorage.setItem(key, string); cb(null); } catch (e) { cb(e); } }, removeItem: function removeItem(key, cb) { try { localStorage.removeItem(key); cb(null); } catch (e) { cb(e); } }, getAllKeys: function getAllKeys(cb) { try { var keys = []; for (var i = 0, len = localStorage.length; i < len; ++i) { keys.push(localStorage.key(i)); } cb(null, keys); } catch (e) { cb(e); } } };