@vovkasm/redux-persist
Version:
persist and rehydrate redux stores
201 lines (164 loc) • 6.05 kB
JavaScript
;
exports.__esModule = true;
exports.default = createPersistor;
var _constants = require('./constants');
var _asyncLocalStorage = require('./defaults/asyncLocalStorage');
var _asyncLocalStorage2 = _interopRequireDefault(_asyncLocalStorage);
var _purgeStoredState = require('./purgeStoredState');
var _purgeStoredState2 = _interopRequireDefault(_purgeStoredState);
var _jsonStringifySafe = require('json-stringify-safe');
var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function createPersistor(store, config) {
// defaults
var serializer = config.serialize === false ? function (data) {
return data;
} : defaultSerializer;
var deserializer = config.serialize === false ? function (data) {
return data;
} : defaultDeserializer;
var blacklist = config.blacklist || [];
var whitelist = config.whitelist || false;
var transforms = config.transforms || [];
var debounce = config.debounce || false;
var keyPrefix = config.keyPrefix !== undefined ? config.keyPrefix : _constants.KEY_PREFIX;
// pluggable state shape (e.g. immutablejs)
var stateInit = config._stateInit || {};
var stateIterator = config._stateIterator || defaultStateIterator;
var stateGetter = config._stateGetter || defaultStateGetter;
var stateSetter = config._stateSetter || defaultStateSetter;
// storage with keys -> getAllKeys for localForage support
var storage = config.storage || (0, _asyncLocalStorage2.default)('local');
if (storage.keys && !storage.getAllKeys) {
storage.getAllKeys = storage.keys;
}
// initialize stateful values
var lastState = stateInit;
var paused = false;
var storesToProcess = [];
var timeIterator = null;
store.subscribe(function () {
if (paused) return;
var state = store.getState();
findStoresToProcess(state).forEach(function (key) {
if (storesToProcess.indexOf(key) !== -1) return;
storesToProcess.push(key);
});
var len = storesToProcess.length;
// time iterator (read: debounce)
if (timeIterator === null) {
timeIterator = setInterval(function () {
if (paused && len === storesToProcess.length || storesToProcess.length === 0) {
clearInterval(timeIterator);
timeIterator = null;
return;
}
var key = storesToProcess.shift();
persistCurrentStateForKey(state, key);
}, debounce);
}
lastState = state;
});
function findStoresToProcess(state) {
var keys = [];
stateIterator(state, function (subState, key) {
if (!passWhitelistBlacklist(key)) return;
var newState = stateGetter(state, key);
if (stateGetter(lastState, key) === newState) return;
keys.push(key);
});
return keys;
}
function persistCurrentStateForKey(state, key) {
var storageKey = createStorageKey(key);
var currentState = stateGetter(state, key);
var endState = transforms.reduce(function (subState, transformer) {
return transformer.in(subState, key);
}, currentState);
if (typeof endState === 'undefined') return null;
return storage.setItem(storageKey, serializer(endState), warnIfSetError(key));
}
function persistCurrentState() {
var state = store.getState();
var promises = storesToProcess.map(function (k) {
return persistCurrentStateForKey(state, k);
});
storesToProcess.splice(0);
return promises;
}
function passWhitelistBlacklist(key) {
if (whitelist && whitelist.indexOf(key) === -1) return false;
if (blacklist.indexOf(key) !== -1) return false;
return true;
}
function adhocRehydrate(incoming) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var state = {};
if (options.serial) {
stateIterator(incoming, function (subState, key) {
try {
var data = deserializer(subState);
var value = transforms.reduceRight(function (interState, transformer) {
return transformer.out(interState, key);
}, data);
state = stateSetter(state, key, value);
} catch (err) {
if (process.env.NODE_ENV !== 'production') console.warn('Error rehydrating data for key "' + key + '"', subState, err);
}
});
} else state = incoming;
store.dispatch(rehydrateAction(state));
return state;
}
function createStorageKey(key) {
return '' + keyPrefix + key;
}
// return `persistor`
return {
rehydrate: adhocRehydrate,
pause: function pause() {
paused = true;
},
resume: function resume() {
paused = false;
},
purge: function purge(keys) {
return (0, _purgeStoredState2.default)({ storage: storage, keyPrefix: keyPrefix }, keys);
},
flush: persistCurrentState
};
}
function warnIfSetError(key) {
return function setError(err) {
if (err && process.env.NODE_ENV !== 'production') {
console.warn('Error storing data for key:', key, err);
}
};
}
function defaultSerializer(data) {
return (0, _jsonStringifySafe2.default)(data, null, null, function (k, v) {
if (process.env.NODE_ENV !== 'production') return null;
throw new Error('\n redux-persist: cannot process cyclical state.\n Consider changing your state structure to have no cycles.\n Alternatively blacklist the corresponding reducer key.\n Cycle encounted at key "' + k + '" with value "' + v + '".\n ');
});
}
function defaultDeserializer(serial) {
return JSON.parse(serial);
}
function rehydrateAction(data) {
return {
type: _constants.REHYDRATE,
payload: data
};
}
function defaultStateIterator(collection, callback) {
return Object.keys(collection).forEach(function (key) {
return callback(collection[key], key);
});
}
function defaultStateGetter(state, key) {
return state[key];
}
function defaultStateSetter(state, key, value) {
state[key] = value;
return state;
}