dai-shi / react-hooks-global-state

[NOT MAINTAINED] Simple global state for React with Hooks API without Context API

Home Page:https://www.npmjs.com/package/react-hooks-global-state

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.

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