Restoring initial state from localStorage, if any?
gremo opened this issue · comments
Hi, first thanks for this great library!
I'm sharing a global state between components, exporting from the globalStage.js
:
import * as actions from './actions';
import { contactModalReducer, newsletterModalReducer, searchModalReducer } from './reducers';
import { createStore } from 'react-hooks-global-state';
// Initial app state
const initialState = {
modalContactsOpen: false,
modalSearchOpen: false,
modalNewsletterOpen: false,
};
const reduceReducers = (...reducers) => (state, action) =>
reducers.reduce((acc, nextReducer) => nextReducer(acc, action), state);
const { dispatch, useGlobalState } = createStore(
reduceReducers(
contactModalReducer,
searchModalReducer,
newsletterModalReducer,
),
initialState
);
export { dispatch, useGlobalState };
I'm wondering... how to restore the initial state from the localStorage
object using createStore
?
hi, createStore
exposes setGlobalState
, so you can use it to set something from anywhere.
setGlobalState('foo', localStorage.getItem('foo'));
Another way is to initialize initialState
with localStorage data.
Hope it helps.
Actually, there's an example: https://github.com/dai-shi/react-hooks-global-state/tree/master/examples/13_persistence
@dai-shi thanks for helping. I got the point, however there is no applyMiddleware
function in react hooks (without using redux). Any idea how to handle the state change persistence?
applyMiddleware
is a simple function that we could create.
The 13_persistence
example is rather to emulate redux pattern.
Let me show the first one as an example, because that's something independent from redux pattern.
const { dispatch, useGlobalState, getGlobalState, setGlobalState } = createStore(
reducer,
initialState
);
const saveToLocalStorage = () => {
localStorage.setItem('foo', JSON.stringify(getGlobalState('foo')));
localStorage.setItem('bar', JSON.stringify(getGlobalState('bar')));
};
const loadFromLocalStorage = () => {
setGlobalState('foo', JSON.parse(localStorage.getItem('foo')));
setGlobalState('bar', JSON.parse(localStorage.getItem('bar')));
};
Notice error handling is omitted.
Sure, I've got the point of loading and saving to local storage (also, bootstrapping from local storage). What is not clear to me is how to update the local storage every time a new action is dispatcher, that is... where to put saveToLocalStorage
without using applyMiddleware
from redux.
Is it just another reducer that doesn't alter the state and it just call the saveToLocalStorage
function?
Oh, sorry. I see your point now. Hm, good question. Without middleware, only way to get notified is the hook. So, I'd do like this:
const { dispatch, useGlobalState: useGlobalStateOrig } = createStore(...);
const useGlobalState = (key) => {
const [state, update] = useGlobalStateOrig(key);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
});
return [state, update];
};
If dispatch is the only way to modify the state, you could also wrap dispatch (which should be better than hacking reducer.)
const dispatch = (action) => {
dispatchOrig(action);
saveToLocalStorage();
};
I understand now, like the last approach. enhancer
could help? I think it's undocumented....
enhancer
could help. I mean applyMiddleware
is the function to take middleware and return an enhancer.
https://github.com/dai-shi/react-hooks-global-state#parameters-1 says enhancer
but it doesn't tell the usage.
BTW, we also have this: https://github.com/dai-shi/react-hooks-global-state/wiki/Persistence
So, I ended up using the following (the example you posted are relative to using a custom reducer and a custom function getFromLocalStorage
inside the createStore
call:
import { getFromLocalStorage, saveToLocalStorage } from './util';
import { createStore } from 'react-hooks-global-state';
import { reduceReducers } from './util';
const initialState = {
cart: {
items: [],
}
};
// This is the main reducer
const reducer = (state, action) => { /* ... */ };
const { dispatch, useGlobalState } = createStore(
reduceReducers(
reducer,
// This additional reducer doesn't alter the state, it just store it
state => {
saveToLocalStorage(LOCAL_STORAGE_KEY, state, LOCAL_STORAGE_TTL_SECONDS);
return state;
}
),
// Bootstrapping from local storage, returning the initialState if storage is empty
getFromLocalStorage(LOCAL_STORAGE_KEY, initialState)
);
export { useGlobalState };
I didn't used a custom useGlobalState
because I would need to typehint all original parameters. I still don't know which approach is better... custom reducer vs overriding the dispatch.
Looks good. I think it's totally valid with the current API and the implementation.
Please close the issue if you don't have further questions. Thanks.