3

I saw a lot of solutions on how to communicate/share data between tabs with Redux/JavaScript with localstorage. Like:

import { applyMiddleware, createStore } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import promise from "redux-promise-middleware";

import reducer from "./reducers";
import {sourceId} from "./utils/helper";

const storageKey = 'redux-local-tab-sync';

function wrapAction(action) {
  return {
    action,
    sourceId,
    time: Date.now()
  }
}

function storageMiddleware() {
  return () => next => action => {
    const wrappedAction = wrapAction(action);

    localStorage.setItem(storageKey, JSON.stringify(wrappedAction));

    next(action);
  }
}

export function createStorageListener(store) {
  return () => {
    const wrappedAction = JSON.parse(localStorage.getItem(storageKey));

    if (wrappedAction.sourceId !== sourceId) {
      console.log(wrappedAction.action)
      store.dispatch(wrappedAction.action);
    }
  }
}

const middleware = applyMiddleware(promise(), thunk, storageMiddleware(), logger());
const store = createStore(reducer, middleware);

window.addEventListener('storage', createStorageListener(store));

export default store;

or Javascript: sharing data between tabs and Communication between tabs or windows .

However all of the solutions which I found have a little problem. They create a ping-pong effect. So when A tab storage changes, then B tab storage will be set, because it gets a message from localstorage. However, because B tab storage was change, it's fires the 'same change event' (but with own information), which was previously sent by A tab. So A tab's storage will be change via local storage event... and everything starts from the beginning.

Is there a better way to handle the syncing of information where:
- A can send message to B [, C, D, ...]; B can send a message to A [, C, D, ...]; etc
- without making ping-pong effect,
- prevent to infinitely change own's storage.

Brett DeWoody
  • 50,328
  • 25
  • 121
  • 168
golddragon007
  • 1,119
  • 11
  • 24
  • As part of the message, set a property containing the 'origin' tab. Then you could easily check if the event originated from the current tab and ignore it. – Brett DeWoody Dec 19 '17 at 12:21
  • Yes, I do it with the 'sourceId'. But this is set with the same way on the opposite tab well, after it's updates it's own store and there it's sets it's own 'sourceId', which will detect the A as new state, however it's A's own state. – golddragon007 Dec 19 '17 at 12:47
  • Actually I need to somehow prevent to send/set back received events into localStorage at line `localStorage.setItem`. But there I don't know where that event's came from... – golddragon007 Dec 19 '17 at 12:57

2 Answers2

2

Maybe you can try this https://github.com/AOHUA/redux-state-sync , broadcast channel is a better way to communicate between tabs.

Bruce Mu
  • 843
  • 9
  • 18
1

Maybe the following will work, it will create a special type of action when triggered from another tab:

function storageMiddleware() {
  return () => next => action => {
    if(action.type!=="from other tab"){
      const wrappedAction = wrapAction(action);
      localStorage.setItem(storageKey, JSON.stringify(wrappedAction));
    }else{
      action=action.data;
    }
    next(action);
  }
}

export function createStorageListener(store) {
  return () => {
    const wrappedAction = JSON.parse(localStorage.getItem(storageKey));

    if (wrappedAction.sourceId !== sourceId) {
      console.log(wrappedAction.action)
      store.dispatch({
        type:"from other tab",
        data:wrappedAction.action
      });
    }
  }
}
HMR
  • 30,349
  • 16
  • 67
  • 136