-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync state across multiple tabs #40
Comments
It should already work like that. For me it syncs the storage across multi tabs. |
It saves across tabs, but does not synchronize. If the store in tab A changes, the store in tab B will only be updated when the page is reloaded. I'm not really sure I'd like such side effects in my store, but that's just my 2c. |
I understand what you are saying, but I find that possibility pretty useful, if you think that a user logs in on a primary tab and the secondary tab gets notified. How it is handled in the secondary tab is up to the application, maybe via a specific action. Now i had to do that manually via a window 'storage' event, what is also ok but could be documented or made optional via settings? |
+1 for this capability, especially because currently changes in an older tab may overwrite updates from the other. |
Here's a snippet that can be used to sync states based on @AirBorne04's suggestion: // actions.ts
const STORAGE = '@ngrx/store/storage';
export class Storage implements Action {
readonly type = STORAGE;
constructor(readonly payload: string) {}
}
// reducers.ts
import { localStorageSync, rehydrateApplicationState } from 'ngrx-store-localstorage';
export function localStorageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
return (state: State, action: All) => {
const keys = ['key1', 'key2'];
if (action.type === STORAGE && keys.includes(action.payload)) {
const rehydratedState = rehydrateApplicationState([action.payload], localStorage, k => k);
return { ...state, ...rehydratedState };
}
return localStorageSync({
keys,
rehydrate: true,
})(reducer)(state, action);
}
}
// app.component.ts
export class AppComponent implements OnInit {
constructor(
private readonly renderer: Renderer2,
private readonly store: Store<State>,
) {}
ngOnInit() {
this.renderer.listen('window', 'storage', event => {
this.store.dispatch(new Storage(event.key));
});
}
} This approach avoids an infinite loop when a storage update in one tab leads to a storage update in another, which in turn causes update in the first one, etc. It also carefully updates only the relevant part of the state, while treating storage as the source of truth. |
Thanks @dinvlad It was helpful for me. |
@dinvlad this works great in dev, however, in production, there is an extra action So, when you reload the page, it logs you out if you were storing token to localStorage, as the token gets wiped out. |
@un33k strange, I haven't experienced that. |
@dinvlad I had the same issue |
@ubcent maybe something recent. In any case, unfortunately I'm moving to Vue/Vuex so may not be of much help.. |
More info here. FYI |
This issue actually exist. Localstorage sync does not update store in application memory as per value updated in localstorge. So when I change store from one tab my localstorage updates. But in another tab the redux (application memory) will still hold the old value. Now if user does not refresh the page and performs some action on site, my localstorage will get updated with the value in redux again and will cause system to misbehave! |
Added the above solution to a known workarounds section in the README |
This seem to no longer work in this form. The Action type does not have a payload. |
Works nicely in the following format. // actions.ts
const storageActionType = '@ngrx/store/storage';
export const storageAction = createAction(
storageActionType,
props<{ payload: string }>()
);
type PayloadAction = Action & {
payload: unknown;
};
// reducers.ts
import { localStorageSync, rehydrateApplicationState } from 'ngrx-store-localstorage';
export const localStorageSyncReducer = (reducer: ActionReducer<any>): ActionReducer<any> => (state: State<any>, action: Action): any => {
const keys = ['key1', 'key2'];
const isPayloadAction = 'payload' in action;
const payloadAction: PayloadAction = action as PayloadAction;
if (action.type === storageActionType && isPayloadAction && keys.includes(payloadAction.payload as string)) {
const rehydratedState = rehydrateApplicationState([payloadAction.payload] as Keys, localStorage, k => k, true);
return { ...state, ...rehydratedState };
}
return localStorageSync({
keys,
rehydrate: true,
})(reducer)(state, action);
};
// app.component.ts
export class AppComponent implements OnInit {
constructor(
private readonly renderer: Renderer2,
private readonly store: Store<State>,
) {}
ngOnInit() {
this.renderer.listen('window', 'storage', event => {
this.store.dispatch(storageAction({ payload: event.key as string }));
});
}
} |
Is there the option to sync the stores through the localstorage across multiple tabs?
The text was updated successfully, but these errors were encountered: