ioof-holdings / redux-dynamic-reducer

Attach reducers to an existing Redux store
BSD 3-Clause "New" or "Revised" License
68 stars 9 forks source link

Redux error when store is initialized with a state #15

Closed siemiatj closed 3 years ago

siemiatj commented 6 years ago

No idea how come no1 ever stumbled upon this, but if you're store is created with reducers, it will throw the typical redux error when trying to attach another reducer, like :

Unexpected keys "foo", "bar", found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "baz". Unexpected keys will be ignored.

Runnable example : https://codesandbox.io/s/465711n60

SOLUTION To work around this you have to create the store without a reducer and just attach it as soon as you can : https://codesandbox.io/s/4zomyv630

In case of a real-life app, I had to export an object with reducers instead of using combineReducers and call createStore with null reducer :

//reducers.js
import { routerReducer as routing } from 'react-router-redux';

import appHandler from './appHandler';
import listHandler from './listHandler';
import menuHandler from './menuHandler';
import windowHandler from './windowHandler';
import pluginsHandler from './pluginsHandler';

export default {
  appHandler,
  listHandler,
  menuHandler,
  windowHandler,
  pluginsHandler,
  routing,
};

// configureStore.js
import { routerMiddleware } from 'react-router-redux';
import { applyMiddleware, compose } from 'redux';
import { createStore } from 'redux-dynamic-reducer';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise';

import rootReducer from '../reducers';

export default function configureStore(history) {
  const middleware = [thunk, promiseMiddleware, routerMiddleware(history)];
  const store = createStore(
    null,
    compose(
      applyMiddleware(...middleware),
      window.devToolsExtension ? window.devToolsExtension() : f => f
    )
  );

  store.attachReducers(rootReducer);

  if (module.hot) {
    module.hot.accept('../reducers', () => {
      const nextReducer = rootReducer;
      store.replaceReducer(nextReducer);
    });
  }

  return store;
}

I'm not sure if there's a way of fixing this, but this workaround might be useful to somebody.

mpeyper commented 6 years ago

Sorry I've taken so long to get to this (just had a new baby).

I'm also surprised no one has reported this before. I've run your example and definitely see the warning, which is surprising, considering how much effort was put into not seeing those errors.

I'll take a closer look at what's going on and see which case was missed.

In the mean time, you may want to try our new library, redux-dynostore as support for this one will cease soon. It supports dynamic reducers and sagas and is more extendible for having more dynamic features added to the store. The dynamic reducer code was heavily based on this library, so it's likely the issue is there as well, but it may have been accidentally fixed in the refactoring.

mpeyper commented 6 years ago

huh, for my own piece of mind, I tried your example out with dynostore and the warning does not appear.

https://codesandbox.io/s/23jn2jj8jj

I guess we did accidentally fix it. Now the frustrating job of figuring out what the difference is.

siemiatj commented 6 years ago

Oh,I thought you can only use dynostore as a HOC for a component, but now I know it works both ways (meaning you can simply call attachReducer). Thanks !

mpeyper commented 6 years ago

No worries!

I'll leave this open in case anyone wants to try and fix it, but for now, the best advice is to migrate to redux-dynostore.

michalzysk commented 6 years ago

+1