rt2zz / redux-persist-crosstab

Keep redux browser tab state in sync
85 stars 28 forks source link

Unable to serialise #4

Open lid2000 opened 8 years ago

lid2000 commented 8 years ago

I'm getting errors using this with redux-persist 2.0 as persistor.rehydrate is being passed an object instead of a string, which causes the deserialise function to die. My workaround has been to parse the newValue value, then stringify the whole statePartial object:

statePartial[keyspace] = JSON.parse(e.newValue); persistor.rehydrate(JSON.stringify(statePartial), function(){ //@TODO handle errors? })

rt2zz commented 8 years ago

Good catch. It seems the most elegant solution is for redux-persist's persistor.rehydrate to detect if the incoming state is a string and deserialize accordingly.

oroce commented 8 years ago

A little bit better solution is writing your own serializer and deserializer:

import { createStore, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import crosstabSync from 'redux-persist-crosstab';
const finalCreateStore = compose(autoRehydrate())(createStore);
const store = finalCreateStore(reducer);

const persistor = persistStore(store, {
  serializer: serializer,
  deserializer: deserializer
});
crosstabSync(persistor);

// this is exactly the same what redux-persist uses
function serializer(data) {
   return JSON.stringify(data);
}
function deserializer(data) {
   // this is the normal deserialization process
   if (typeof data === 'string') {
      return JSON.parse(data);
   }
   // basically we need to deserialize this one:
   // var o = {
   //     f: '{"foo":"bar"}',
   //     b: '{"bar":"foo"}'
   // };

   return Object.keys(data).reduce(function(obj, key) {
     obj[key] = JSON.parse(data[key]);
     return obj;
   }, {});
}

Or we could modify redux-persist-crosstab to support serialization/deserialization:

var constants = require('redux-persist/constants')
var keyPrefix = constants.keyPrefix

module.exports = function(persistor, config){
  var config = config || {}
  var blacklist = config.blacklist || false
  var whitelist = config.whitelist || false
  var serialize = config.serialize || function(data) {
     return JSON.stringify(data)
  }
  var deserialize = config.deserialize || function(data) {
    return JSON.parse(data)
  }
  window.addEventListener('storage', handleStorageEvent, false)

  function handleStorageEvent(e){
    if(e.key.indexOf(keyPrefix) === 0){
      var keyspace = e.key.substr(keyPrefix.length)
      if(whitelist && whitelist.indexOf(keyspace) === -1){ return }
      if(blacklist && blacklist.indexOf(keyspace) !== -1){ return }

      var statePartial = {}
      statePartial[keyspace] = deserialize(e.newValue)
      persistor.rehydrate(serialize(statePartial), function(){
        //@TODO handle errors?
      })
    }
  }
}

First one looks uglier but the latter one can hurt the performance :(

So probably the best would be having support in redux-persist for rehydrating serialized data.