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

Loading from the storage does not overwrite initial state completely #4

Open
georgjaehnig opened this issue Apr 28, 2017 · 6 comments

Comments

@georgjaehnig
Copy link

Hi, it seems that loading from the storage does not overwrite the initial state completely, and that looks like a bug for me. ( If it's not, please tell me how I can work with it productively. :) )

An example: Let's say, this is my initial state:

state = {
  ...
  test: {
    foo: 1,
  }
  ...
}

Then, in some dispatch, I change the value of test:

nextState.test = { bar: 2 };

With redux-storage set up, this is saved (I guess).

When I now close/reopen or reload the whole app, I have this state:

state = {
  ...
  test: {
    bar: 2,
    foo: 1,
  }
  ...
}

I think, there should be no foo: 1 at this point, should it?

At least it's bugging me in my app, so what's the correct way of getting rid of it? Is there a config option for redux-storage that I can set?

(See the whole code here.)

@jmfrancois
Copy link

AFAIK the load give you the state, so it's up to you to write a reducer to update the current state created by the create store.

I have also try a different workaround: Put a fake store to call the loader, then init the store and pass the persisted state as an 'initial state'.

Because it's the first time I'm using it, I'm not sure this is the way to go, but nothing in the load state API is written to update the current state.

Just remember to call load the store should exists so there is already a state.

@jsardev
Copy link

jsardev commented Jun 6, 2017

I came across the same issue @georgjaehnig and I've also expected that the state will be overwritten. And I also don't know if it's a lack of understanding or just a bug :D But looks more likely like the second one.

@jmfrancois If the load function would just give you the state it should also not modify anything in the state (but it does) and also it should not need any store object to function properly.

@chandlervdw
Copy link

+1, this is very confusing. The previous version this was forked from definitely loaded the stored state upon calling load(store). This seems to fire the REDUX_STORAGE_LOAD action but doesn't actually update the redux state... is it required to import LOAD from redux-storage in my own reducer?

@chandlervdw
Copy link

Actually, my issue is that when I try to introduce redux-devtools-extension, it breaks the actual loading of the storage...

@unleashit
Copy link

unleashit commented Aug 17, 2017

It's working for me, including redux devtools. My setup in case anyone's interested:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import axios from 'axios';
import * as storage from 'redux-storage';
import createEngine from 'redux-storage-engine-localstorage';
// reducers are exported already combined
import combinedReducers from '../reducers';

// my app is server rendered so initialState would be a rehydrated object
function configureStore(initialState) {
  // Init localstorage provider
  const engine = createEngine('my-store');
  const reducer = storage.reducer(combinedReducers);
  const lsMiddleware = storage.createMiddleware(engine);
  const load = storage.createLoader(engine);

  const enhancers = compose(
    // Middleware store enhancer.
    applyMiddleware(
      // redux thunk
      thunk.withExtraArgument({ axios }),
      // localstorage middleware
      lsMiddleware,
    ),
    // Redux Dev Tools
    process.env.NODE_ENV === 'development' &&
      typeof window !== 'undefined' &&
      typeof window.devToolsExtension !== 'undefined'
      ? // Call the brower extension function to create the enhancer.
        window.devToolsExtension()
      : // Else we return a no-op function.
        f => f,
  );


  const cachedStore = typeof window !== 'undefined'
    ? !!localStorage.getItem('my-store')
    : false;

  const store = initialState && !cachedStore
    ? createStore(reducer, initialState, enhancers)
    : createStore(reducer, enhancers);

  if (cachedStore) {
    load(store);
  }

// only needed if you're using hot module replacement
  if (process.env.NODE_ENV === 'development' && module.hot) {
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers').default; // eslint-disable-line global-require

      store.replaceReducer(nextRootReducer);
    });
  }

  return store;
}

export default configureStore;

@anwarhamr
Copy link

anwarhamr commented Dec 11, 2017

I was having the same issue, but found it to be a copy/paste mistake. I missed/overlooked/forgot to add the reducer created by storage.reducer(rootReducer) to my call to createStore call. Loading the reducer is the key to all of this happening.

One other issue I've found that you want to be careful of, if you persist complex objects for instance Moment, you will need to handle the LOAD event in your reducer and convert it to an object. You can also probably do this using the optional parameters replacer, reviver to createEngine if it supports them; redux-storage-engine-localstorage does.

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

6 participants