createPersistoid.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { KEY_PREFIX, REHYDRATE } from './constants';
  2. // @TODO remove once flow < 0.63 support is no longer required.
  3. export default function createPersistoid(config) {
  4. // defaults
  5. var blacklist = config.blacklist || null;
  6. var whitelist = config.whitelist || null;
  7. var transforms = config.transforms || [];
  8. var throttle = config.throttle || 0;
  9. var storageKey = "".concat(config.keyPrefix !== undefined ? config.keyPrefix : KEY_PREFIX).concat(config.key);
  10. var storage = config.storage;
  11. var serialize;
  12. if (config.serialize === false) {
  13. serialize = function serialize(x) {
  14. return x;
  15. };
  16. } else if (typeof config.serialize === 'function') {
  17. serialize = config.serialize;
  18. } else {
  19. serialize = defaultSerialize;
  20. }
  21. var writeFailHandler = config.writeFailHandler || null; // initialize stateful values
  22. var lastState = {};
  23. var stagedState = {};
  24. var keysToProcess = [];
  25. var timeIterator = null;
  26. var writePromise = null;
  27. var update = function update(state) {
  28. // add any changed keys to the queue
  29. Object.keys(state).forEach(function (key) {
  30. if (!passWhitelistBlacklist(key)) return; // is keyspace ignored? noop
  31. if (lastState[key] === state[key]) return; // value unchanged? noop
  32. if (keysToProcess.indexOf(key) !== -1) return; // is key already queued? noop
  33. keysToProcess.push(key); // add key to queue
  34. }); //if any key is missing in the new state which was present in the lastState,
  35. //add it for processing too
  36. Object.keys(lastState).forEach(function (key) {
  37. if (state[key] === undefined && passWhitelistBlacklist(key) && keysToProcess.indexOf(key) === -1 && lastState[key] !== undefined) {
  38. keysToProcess.push(key);
  39. }
  40. }); // start the time iterator if not running (read: throttle)
  41. if (timeIterator === null) {
  42. timeIterator = setInterval(processNextKey, throttle);
  43. }
  44. lastState = state;
  45. };
  46. function processNextKey() {
  47. if (keysToProcess.length === 0) {
  48. if (timeIterator) clearInterval(timeIterator);
  49. timeIterator = null;
  50. return;
  51. }
  52. var key = keysToProcess.shift();
  53. var endState = transforms.reduce(function (subState, transformer) {
  54. return transformer.in(subState, key, lastState);
  55. }, lastState[key]);
  56. if (endState !== undefined) {
  57. try {
  58. stagedState[key] = serialize(endState);
  59. } catch (err) {
  60. console.error('redux-persist/createPersistoid: error serializing state', err);
  61. }
  62. } else {
  63. //if the endState is undefined, no need to persist the existing serialized content
  64. delete stagedState[key];
  65. }
  66. if (keysToProcess.length === 0) {
  67. writeStagedState();
  68. }
  69. }
  70. function writeStagedState() {
  71. // cleanup any removed keys just before write.
  72. Object.keys(stagedState).forEach(function (key) {
  73. if (lastState[key] === undefined) {
  74. delete stagedState[key];
  75. }
  76. });
  77. writePromise = storage.setItem(storageKey, serialize(stagedState)).catch(onWriteFail);
  78. }
  79. function passWhitelistBlacklist(key) {
  80. if (whitelist && whitelist.indexOf(key) === -1 && key !== '_persist') return false;
  81. if (blacklist && blacklist.indexOf(key) !== -1) return false;
  82. return true;
  83. }
  84. function onWriteFail(err) {
  85. // @TODO add fail handlers (typically storage full)
  86. if (writeFailHandler) writeFailHandler(err);
  87. if (err && process.env.NODE_ENV !== 'production') {
  88. console.error('Error storing data', err);
  89. }
  90. }
  91. var flush = function flush() {
  92. while (keysToProcess.length !== 0) {
  93. processNextKey();
  94. }
  95. return writePromise || Promise.resolve();
  96. }; // return `persistoid`
  97. return {
  98. update: update,
  99. flush: flush
  100. };
  101. } // @NOTE in the future this may be exposed via config
  102. function defaultSerialize(data) {
  103. return JSON.stringify(data);
  104. }