How to serialize and deserialize in ngrx-store-localstorage library

This blog shows how to write your own customized serializer and deserializer in ngrx-store-localstorage library.

A problem I got: I want to put a state value into local storage. I use ngrx-store-localstorage library and set rehydrate to true. However, I keep getting console errors. Then I realize because I am using Set and it does not work well with JSON.stringify. For example, if you run:

JSON.stringify(new Set([])) // would return "{}"JSON.stringify(new Set(['a', 'b', 'c'])) // would still return "{}"

This is what my store state looks like:

export interface AppState {
user: {
userId: string;
likedPostIds: Set<string>;
}
}

And this is what my reducer looks like:

export function userId(state: string, action: SetUserId): string {
switch (action.type) {
case UserActions.SetUserId:
return action.userId;
default:
return state;
}
}

export function likePostIds(state: Set<string>, action: any): Set<string> {
switch (action.type) {
case UserActions.LikePost:
return state.add(action.postId);
case UserActions.UnlikePost:
state.delete(action.postId);
return state;
default:
return state;
}
}

export const userReducer = combineReducers({ userId, likePostIds });

I want to keep the type of likePostIds to be Set because the reducer code is much cleaner with Set.

I need to write customized serializer and deserializer.

// serializer: converts UserState to serializable JSON passing to JSON.stringify
export function userStateToJson(userState: UserState): Record<string, any> {
const state = clone(userState);
if (state.likePostIds && state.likePostIds instanceof Set) {
state.likePostIds = Array.from(state.likePostIds);
}
return state;
}

// deserializer: converts JSON from JSON.parse to UserState
export function jsonToUserState(state: Record<string, any>): UserState {
try {
if (!(state.likePostIds instanceof Set)) {
state.likePostIds = new Set(state.likePostIds);
}
} catch (_) {
state.likePostIds = new Set([]);
}
return state as UserState;
}

Now I have to update reducer for localStorageSync:

export function localStorageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
return localStorageSync({
keys: [
{ user: { serialize: userStateToJson, deserialize: jsonToUserState } }
],
rehydrate: true
})(reducer);
}

Finally, my state is properly synced with local storage.

A frontend web developer in Toronto

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store