aohua / redux-state-sync

A lightweight middleware to sync your redux state across browser tabs
MIT License
233 stars 29 forks source link

Broadcast channels not systematically closed ? Leak ? #128

Open odusseys opened 2 years ago

odusseys commented 2 years ago

I am running into a very strange bug, which I am having trouble reproducing consistently. For background I am using the initStateWithPrevTab' helper to reinitialize state with previous tabs upon page load. My overall code looks like this (I have only one reducer,dataFetching`, and corresponding actions which only act on that reducer, which are whitelisted by redux-state-sync)

const persistConfig: PersistConfig<RootState> = {
  key: "root",
  storage,
  timeout: 0,
  stateReconciler: autoMergeLevel1,
  blacklist: ["dataFetching"],
};

const persistedReducer = persistReducer(
  persistConfig,
  withReduxStateSync(rootReducer) as any
);

const dataFetchingSync = createStateSyncMiddleware({
  whitelist: DATA_FETCHING_ACTION_TYPES,
});

export const store = createStore(
  persistedReducer,
  applyMiddleware(logger, dataFetchingSync)
);

initStateWithPrevTab(store);

Occasionally, I will run into issues when reloading a page where the redux state is immediately reset to its previous value, including the non-persisted reducer, which seemed strange. Upon investigation, what happens is the following

This means that there is no way, when that happens, to refresh the state in memory. Closing all tabs does not resolve it. Closing Chrome, however, does - and no more _RECEIVE_INIT_STATE action is received

I cannot reproduce this consistently and it happens randomly. I cannot see a simple explanation for this, but I don't understand how, after reload, the state can still be available somewhere, and dispatched through the BroadcastChannel. I can't find a trace of that state anywhere in application storage so it must be in memory. I am wondering if, by any chance, the BroadcastChannel created is not being closed upon reload, thus leading to a leak where, after reloading, it is still present with its original listener, and dispatches a state which has been kept in memory. The BroadCastChannel API docs explicitly say that the channel should be closed when no longer used in order to avoid memory leaks. This does not seem to be the case in the source code. Should there perhaps be a listener for window.unload to close the channel ?

aohua commented 2 years ago

Hi @odusseys Could you try using localstorage as the communication channel between tabs? If the issue still appears, it might be something wrong with BroadcastChannel, but I feel by the time you reload the BroadcastChannel should be closed and reopened.

odusseys commented 2 years ago

I'm not sure it is. https://github.com/pubkey/broadcast-channel#close-the-channel-if-you-do-not-need-it-anymore

Now, on browsers which support BrodcastChannel, the lib uses the native, API. This also requires the channel to be closed to allow for garbage collection https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel/close

So I think a listener for window.unload which closes the channel connection would be necessary here

mkdudeja commented 5 months ago

Hello Everyone,

Any luck with above issue? Facing a similar issue where my only opened tab somehow gets synced with some other and causing the state to be replaced with earlier version. And, its intermittent.

Thanks