Closed peter-lyon closed 6 years ago
The code you put here is pretty much the same as in flash-cards app, and it works in flash-cards. So I think somewhere else in your app is causing the issue, like resetting the state that you don't expected. Try putting a breakpoint in your reducer and see if you can spot where the state is getting modified incorrectly.
Is not "my code", is your code loadState(), // If there is local storage data, load it.
Don't cause any effects. No exceptions, no logs.
Any idea that can help me? Thanks!
I am observing the same results as described by peter. Used the PWA started kit with the basic source from here: https://github.com/Polymer/pwa-starter-kit/tree/template-typescript and using the basic counter example to store the state in local storage for the counter element.
added localStorage.ts:
export const saveState = (state) => {
let stringifiedState = JSON.stringify(state);
localStorage.setItem('__wtt_store__', stringifiedState);
}
export const loadState = () => {
try {
let json = localStorage.getItem('__wtt_store__') || '{}';
let state = JSON.parse(json);
console.log("local storage state:"+state.counter.clicks);
if (state) {
return state;
} else {
return undefined; // To use the defaults in the reducers
}
} catch {
console.log("local storage error");
return undefined;
}
}
Changed store.ts template to
declare global {
interface Window {
process?: Object;
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
import {
createStore,
compose,
applyMiddleware,
combineReducers,
Reducer,
StoreEnhancer
} from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk';
import { lazyReducerEnhancer } from 'pwa-helpers/lazy-reducer-enhancer.js';
import app, { AppState } from './reducers/app.js';
import { CounterState } from './reducers/counter.js';
import { ShopState } from './reducers/shop.js';
import { AppAction } from './actions/app.js';
import { CounterAction } from './actions/counter.js';
import { ShopAction } from './actions/shop.js';
import { saveState, loadState } from './localstorage.js';
// Overall state extends static states and partials lazy states.
export interface RootState {
app?: AppState;
counter?: CounterState;
shop?: ShopState;
}
export type RootAction = AppAction | CounterAction | ShopAction;
// Sets up a Chrome extension for time travel debugging.
// See https://github.com/zalmoxisus/redux-devtools-extension for more information.
const devCompose: <Ext0, Ext1, StateExt0, StateExt1>(
f1: StoreEnhancer<Ext0, StateExt0>, f2: StoreEnhancer<Ext1, StateExt1>
) => StoreEnhancer<Ext0 & Ext1, StateExt0 & StateExt1> =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// Initializes the Redux store with a lazyReducerEnhancer (so that you can
// lazily add reducers after the store has been created) and redux-thunk (so
// that you can dispatch async actions). See the "Redux and state management"
// section of the wiki for more details:
// https://github.com/Polymer/pwa-starter-kit/wiki/4.-Redux-and-state-management
export const store = createStore(
state => state as Reducer<RootState, RootAction>,
//(state, action) => state,
loadState(), // If there is local storage data, load it.
devCompose(
lazyReducerEnhancer(combineReducers),
applyMiddleware(thunk as ThunkMiddleware<RootState, RootAction>))
);
// Initially loaded reducers.
store.addReducers({
app
});
// This subscriber writes to local storage anytime the state updates.
store.subscribe(() => {
saveState(store.getState());
});
The console log shows me that state is written and loaded. But inital data values of the components are back to initial values after reload - however, the localstorage is there with correct values?
Not sure what is missing here? Maybe something with the INITIAL_STATE?
const counter: Reducer<CounterState, RootAction> = (state = INITIAL_STATE, action) => { ... }
Consider loading state from localstorage as an action, not when creating the store. In pwa-starter-kit-hn, there's a loadFavorites
action creator that dispatches after indexedDB is ready (indexedDB loading being async necessitates this pattern, but it can also be used with localstorage).
The saveState method is replacing the loaded state on refresh, in order to avoid that you have to persist the loaded state by something like:
export const saveState = state => {
try {
const loadedState = loadState() || {};
const serializedState = JSON.stringify(mergeDeep(loadedState, state));
localStorage.setItem('state', serializedState);
} catch (err) {
// Ignore write errors.
}
};
and when you add a new reducer that you want to persist its loaded state, you have to do something like:
const loadedState = loadState() || {};
store.addReducers({
app: (state, actions) => app(loadedState.app || state, actions)
});
To understand how redux works and how the state can be saved in localStorage, I am trying to implement a system similar to "Flash Cards", with a new module "localStorage.js". The state is correctly saved in localStorage and does not throw any exceptions when loading the status from localStorage, but all values are initialized to their default value even when hardcoding a JSON.
Any ideas to make this work?
localStorage.js:
store.js: