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

Fix #6 - do not mutate vuex store outside mutation handlers #64

Merged
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
30 changes: 24 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface Options {
key?: string;
}

export default function(options?: Options) {
export default function (options?: Options) {
const tab = new Tab(window);
let key: string = 'vuex-multi-tab';
let statesPaths: string[] = [];
Expand All @@ -18,19 +18,37 @@ export default function(options?: Options) {

function filterStates(state: { [key: string]: any }): { [key: string]: any } {
const result = {};
statesPaths.forEach(statePath => {
statesPaths.forEach((statePath) => {
set(statePath, pick(statePath, state), result);
});
return result;
}

function mergeState(oldState: object, newState: object) {
/**
* simple object deep clone method
* @param obj
*/
function cloneObj(obj: any): any {
if (Array.isArray(obj)) {
return obj.map((val) => cloneObj(val));
} else if (typeof obj === 'object' && obj !== null) {
return Object.keys(obj).reduce((r: any, key) => {
r[key] = cloneObj(obj[key]);
return r;
}, {});
}
return obj;
}

function mergeState(oldState: any, newState: object) {
// if whole state is to be replaced then do just that
if (statesPaths.length === 0) return { ...newState };
// else take old state
const merged = { ...oldState };

// else clone old state
const merged: any = cloneObj(oldState);

// and replace only specified paths
statesPaths.forEach(statePath => {
statesPaths.forEach((statePath) => {
const newValue = pick(statePath, newState);
// remove value if it doesn't exist, overwrite otherwise
if (typeof newValue === 'undefined') remove(statePath, merged);
Expand Down
50 changes: 50 additions & 0 deletions test/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,56 @@ describe('vuex-multi-tab-state basic tests', () => {
expect(spy).to.have.been.called.with(testState.state);
});

it('should fetch filtered nested modules state from local storage without no-state-mutation errors', () => {
const testState = {
id: 'randomIdHere',
state: {
random: 1,
modA: {
rainbow: [1, 2, 3],
some: {
value1: 3,
value2: 4,
},
},
},
};
window.localStorage.setItem('vuex-multi-tab', JSON.stringify(testState));

const store = new Vuex.Store({
strict: true,
state: { random: 0 },
modules: {
modA: {
//namespaced: true,
state: {
rainbow: [],
some: {
value1: 0,
value2: 0,
},
},
},
},
});

const plugin = createMultiTabState({
statesPaths: ['random', 'modA.rainbow', 'modA.some.value1'],
});

plugin(store);
expect(store.state).to.be.eql({
random: 1,
modA: {
rainbow: [1, 2, 3],
some: {
value1: 3,
value2: 0, // not set during read-from-storage
},
},
});
});

it('should save only the specified states in local storage', () => {
const store = new Vuex.Store({
state: { bar: { random: 0, rainbow: 0 }, foo: 0 },
Expand Down