Search code examples
javascriptreactjsecmascript-6reduxflux

Can you, or should you use localStorage in Redux's initial state?


For example...

export const user = (state = {
  id: localStorage.getItem('id'),
  name: localStorage.getItem('name'),
  loggedInAt: null
}, action) => {
    case types.LOGIN:
      localStorage.setItem('name', action.payload.user.name);
      localStorage.setItem('id', action.payload.user.id);
      return { ...state, ...action.payload.user }

    default:
      return {  ...state, loggedInAt: Date.now() }
}

That's a scaled down version of what I'm doing, default returns the state from localStorage as expected. However the state of my application is actually blank once I refresh the page.


Solution

  • Redux createStore 2nd param is intended for store initialization:

    createStore(reducer, [initialState], [enhancer])
    

    So you can do something like this:

    const initialState = {
      id: localStorage.getItem('id'),
      name: localStorage.getItem('name'),
      loggedInAt: null
    };
    
    const store = createStore(mainReducer, initialState);
    

    Since reducers should be pure functions (i.e. no side effects) and localStorage.setItem is a side effect, you should avoid saving to localStorage in a reducer.

    Instead you can:

    store.subscribe(() => {
        const { id, name } = store.getState();
    
        localStorage.setItem('name', name);
        localStorage.setItem('id', id);
    });
    

    This will happen whenever the state changes, so it might affect performance.

    Another option is to save the state only when the page is closed (refresh counts) using onBeforeUnload:

    window.onbeforeunload = () => {
        const { id, name } = store.getState();
    
        localStorage.setItem('name', name);
        localStorage.setItem('id', id);
    };