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

Restoring initial state from localStorage, if any? #49

Closed
gremo opened this issue Sep 27, 2020 · 10 comments
Closed

Restoring initial state from localStorage, if any? #49

gremo opened this issue Sep 27, 2020 · 10 comments

Comments

@gremo
Copy link

gremo commented Sep 27, 2020

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?

@dai-shi
Copy link
Owner

dai-shi commented Sep 27, 2020

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.

@dai-shi
Copy link
Owner

dai-shi commented Sep 27, 2020

@gremo
Copy link
Author

gremo commented Sep 28, 2020

@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?

@dai-shi
Copy link
Owner

dai-shi commented Sep 28, 2020

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.

@gremo
Copy link
Author

gremo commented Sep 28, 2020

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?

@dai-shi
Copy link
Owner

dai-shi commented Sep 28, 2020

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();
};

@gremo
Copy link
Author

gremo commented Sep 28, 2020

I understand now, like the last approach. enhancer could help? I think it's undocumented....

@dai-shi
Copy link
Owner

dai-shi commented Sep 28, 2020

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

@gremo
Copy link
Author

gremo commented Sep 28, 2020

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.

@dai-shi
Copy link
Owner

dai-shi commented Sep 28, 2020

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.

@gremo gremo closed this as completed Sep 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants