Skip to content

Commit 0327a1f

Browse files
Merge pull request #55 from ixnv/save-user-preferences-to-localstorage
Save user preferences to localStorage
2 parents a898318 + 6d061a1 commit 0327a1f

File tree

5 files changed

+129
-4
lines changed

5 files changed

+129
-4
lines changed

Diff for: .eslintrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"env": {
55
"browser": true,
66
"node": true,
7-
"jest": true
7+
"jest": true,
8+
"es6": true
89
},
910
"settings": {
1011
"react": {

Diff for: __tests__/localStorage.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {
2+
loadState,
3+
saveState
4+
} from '../src/helpers/localStorage';
5+
6+
describe('LocalStorage utilities', () => {
7+
beforeEach(() => {
8+
window.localStorage.clear();
9+
});
10+
11+
const data = {
12+
foo: 1
13+
};
14+
15+
it('should save and load state', () => {
16+
saveState(data);
17+
18+
const state = loadState();
19+
expect(state).toMatchObject(data);
20+
});
21+
22+
it('should not throw exceptions if localStorage is undefined', () => {
23+
Reflect.deleteProperty(window, 'localStorage');
24+
25+
expect(saveState(data)).toBeUndefined();
26+
expect(loadState()).toBeUndefined();
27+
});
28+
});

Diff for: __tests__/store/store.test.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
TOGGLE_JS,
3+
TOGGLE_MODE
4+
} from '../../src/static/constants/actions';
5+
import { loadState, saveState } from '../../src/helpers/localStorage';
6+
7+
describe('Store', () => {
8+
beforeEach(() => {
9+
jest.resetModules();
10+
window.localStorage.clear();
11+
});
12+
13+
it('should work without saved state', () => {
14+
const {
15+
default: store,
16+
initialState
17+
} = require('../../src/store');
18+
19+
expect(store.getState()).toMatchObject(initialState);
20+
});
21+
22+
it('should load saved state from localStorage', () => {
23+
const savedState = {
24+
mode: 'light',
25+
js: 'es6'
26+
};
27+
saveState(savedState);
28+
29+
const state = require('../../src/store').default.getState();
30+
expect(state.mode).toBe('light');
31+
expect(state.js).toBe('es6');
32+
});
33+
34+
it('should save state to localStorage', () => {
35+
const toggleJSAction = {
36+
type: TOGGLE_JS,
37+
payload: 'es6'
38+
};
39+
40+
const toggleModeAction = {
41+
type: TOGGLE_MODE,
42+
payload: 'light'
43+
};
44+
45+
const {
46+
default: store
47+
} = require('../../src/store');
48+
49+
store.dispatch(toggleJSAction);
50+
store.dispatch(toggleModeAction);
51+
52+
expect(loadState()).toMatchObject({
53+
mode: 'light',
54+
js: 'es6'
55+
});
56+
});
57+
});

Diff for: src/helpers/localStorage.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const loadState = () => {
2+
try {
3+
const serializedState = JSON.parse(localStorage.getItem('state'));
4+
5+
return serializedState === null ? undefined : serializedState;
6+
} catch (e) {
7+
return undefined;
8+
}
9+
};
10+
11+
export const saveState = (state) => {
12+
try {
13+
const serializedState = JSON.stringify(state);
14+
localStorage.setItem('state', serializedState);
15+
} catch (e) {
16+
return undefined;
17+
}
18+
};

Diff for: src/store/index.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
import { createStore, applyMiddleware, compose } from 'redux';
1+
import {
2+
createStore,
3+
applyMiddleware,
4+
compose
5+
} from 'redux';
26
const uuid = require('uuid/v4');
37
import reducer from '../reducers/index';
48
import patterns from '../static/patterns';
59
import middleware from '../middleware';
10+
import {
11+
loadState,
12+
saveState
13+
} from '../helpers/localStorage';
614

715
export const answers = patterns.map(pattern => ({
816
...pattern,
@@ -18,16 +26,29 @@ export const initialProgress = {
1826
current: answers[0]
1927
};
2028

21-
const initialState = {
29+
export const initialState = {
2230
js: 'es5',
2331
mode: 'dark',
2432
intro: true,
2533
patterns: answers,
2634
progress: initialProgress
2735
};
2836

37+
const state = {
38+
...initialState,
39+
...loadState()
40+
};
41+
2942
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
3043

31-
const store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(...middleware)));
44+
const store = createStore(reducer, state, composeEnhancers(applyMiddleware(...middleware)));
45+
46+
store.subscribe(() => {
47+
const currentState = store.getState();
48+
saveState({
49+
mode: currentState.mode,
50+
js: currentState.js
51+
});
52+
});
3253

3354
export default store;

0 commit comments

Comments
 (0)