Skip to content
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

Add support for merging intitial state into rehydrated state #84

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
"peerDependencies": {
"@ngrx/store": "^4.0.0 || ^5.0.0"
},
"dependencies": {
"deepmerge": "^2.1.0"
},
"devDependencies": {
"@angular/core": "^2.4.7",
"@ngrx/core": "^1.2.0",
Expand Down
32 changes: 31 additions & 1 deletion spec/index_spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
declare var beforeEachProviders, it, describe, expect, inject;
require('es6-shim');
import { syncStateUpdate, rehydrateApplicationState, dateReviver } from '../src/index';
import { syncStateUpdate, rehydrateApplicationState, dateReviver, mergeInitialState } from '../src/index';
import * as CryptoJS from 'crypto-js';

// Very simple classes to test serialization options. They cover string, number, date, and nested classes
Expand Down Expand Up @@ -374,4 +374,34 @@ describe('ngrxLocalStorage', () => {
expect(t1 instanceof TypeA).toBeTruthy();
expect(finalState.simple instanceof TypeA).toBeFalsy();
});

it('mergeInitialState', () => {
// test that nested object are merged, not overwritten
let s = new MockStorage();
let skr = mockStorageKeySerializer;
let initialState = { state: { stored: false, not_stored: false }};
let keys = [{ state: ['stored']}];
let mock_action = { type: '@ngrx/store/init' };

// write the initial state,
// key "not_stored" will not be included in hydrated state
syncStateUpdate(initialState, keys, s, skr, false);
let rehydratedState: any = rehydrateApplicationState(keys, s, skr, true);
expect(rehydratedState.state.stored).toEqual(false);
expect(rehydratedState.state.not_stored).not.toBeDefined();

// update this state,
// "not_stored" still not present
let newState = { state: { ...initialState.state, stored: true, not_stored: true } };
syncStateUpdate(newState, keys, s, skr, false);

let newRehydratedState: any = rehydrateApplicationState(keys, s, skr, true);
expect(newRehydratedState.state.stored).toEqual(true);
expect(newRehydratedState.state.not_stored).not.toBeDefined();

// merge state with initialstate. now "not_stored" is included with default value
let mergedState = mergeInitialState({}, initialState, newRehydratedState, mock_action);
expect(mergedState.state.stored).toEqual(true);
expect(mergedState.state.not_stored).toEqual(false);
});
});
21 changes: 15 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as merge from 'deepmerge';

const INIT_ACTION = '@ngrx/store/init';
const UPDATE_ACTION = '@ngrx/store/update-reducers';
const detectDate = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
Expand Down Expand Up @@ -228,12 +230,8 @@ export const localStorageSync = (config: LocalStorageConfig) => (
Handle case where state is rehydrated AND initial state is supplied.
Any additional state supplied will override rehydrated state for the given key.
*/
if (
(action.type === INIT_ACTION || action.type === UPDATE_ACTION) &&
rehydratedState
) {
state = Object.assign({}, state, rehydratedState);
}
state = mergeInitialState(state, config.initialState, rehydratedState, action);

const nextState = reducer(state, action);
syncStateUpdate(
nextState,
Expand All @@ -246,6 +244,16 @@ export const localStorageSync = (config: LocalStorageConfig) => (
};
};

export const mergeInitialState = (state, initialState, rehydratedState, action) => {
if (
(action.type === INIT_ACTION || action.type === UPDATE_ACTION) &&
rehydratedState
) {
return merge.all([state, initialState || {}, rehydratedState]);
}
return state;
}

/*
@deprecated: Use localStorageSync(LocalStorageConfig)

Expand All @@ -272,6 +280,7 @@ export interface LocalStorageConfig {
keys: any[];
rehydrate?: boolean;
storage?: Storage;
initialState?: any;
removeOnUndefined?: boolean;
restoreDates?: boolean;
storageKeySerializer?: (key: string) => string;
Expand Down