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.