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

Reducer is called with previously performed actions when asynchronously loading another reducer #205

Closed
arjunu opened this issue Sep 6, 2016 · 7 comments

Comments

@arjunu
Copy link

arjunu commented Sep 6, 2016

Seeing a weird bug only when I use the extension (2.5.1.9). Not using it with Vanilla DevTools. Have followed instructions in the README to set it up.

The bug is: my reducer is called with previous actions when asynchronously loading another reducer.

Say I perform actions say ACTION_X and ACTION_Y in a page. I leave the page and go to another where the new page's reducer is injected. When this happens the previous page's reducer is called with all the actions I performed in that page (ACTION_X & ACTION_Y).

I can confirm the actions are not dispatched from my code after I leave the page and also they do not show up in the extension. I think it's caused because of the reducer injection. Because if I navigate to a page which does not have a reducer this won't happen.

What's weird about this is it happens only for two of the 5 actions I have in the page. Have not observed it in other pages.

Any help to debug this further is much appreciated.

My app uses React Router with Webpack code splitting and I'm asynchronously injecting reducers on a route load like this:

{
     path: "somePath",
     getComponent(location, cb) {
         require.ensure([],
             require => {
                 injectReducer('reducerName', require('./pages/SomePage/SomePage.reducer').default);
                     cb(null, CreateModel);
                  }
              )
            }
       }
}

where injectReducer is:

function injectAsyncReducer(store) {
    return (name, asyncReducer) => {
        store.asyncReducers[name] = asyncReducer; 
        store.replaceReducer(createReducer(store.asyncReducers));
    };
}

injectReducer = injectAsyncReducer(store);

and my createReducer looks like this

export default function createReducer(asyncReducers) {
    const appReducer = combineReducers({
        ...asyncReducers
    });

    return (state, action) => {
        return appReducer(state, action)
    };
};
@arjunu arjunu changed the title Reducer is called with previous actions when asynchronously loading another reducer Reducer is called with previously performed actions when asynchronously loading another reducer Sep 6, 2016
@zalmoxisus
Copy link
Owner

zalmoxisus commented Sep 6, 2016

Yes, it's as expected and the same problem is with vanilla Redux DevTools.

We don't support replaceReducer, because we use it for hot reloading, so when it happens, redux-devtools-instrument recomputes the action history. In case you changed something inside you reducers, with hot reloading you get the updated reducers recomputed and see the new state (otherwise you have to refresh the page and do all the previous actions again).

Not sure whether we want to solve this on Redux part (adding a second argument for replaceReducer), redux-devtools-instrument part (detecting somehow whether it's caused by HMR), or on the extension part (adding a new method to API to commit the action history together with replaceReducer). Any suggestions are welcome. /cc @gaearon

@zalmoxisus
Copy link
Owner

A simple solution would be to introduce Cycle mode and recompute reducers for replaceReducer only when this mode is activated. So, one will select this mode only when working on a specific feature, exactly as @gaearon described.

@arjunu
Copy link
Author

arjunu commented Sep 6, 2016

Where I can read about using Cycle mode?

@zalmoxisus
Copy link
Owner

@arjunu, here. It's just an idea yet.

@arjunu
Copy link
Author

arjunu commented Sep 6, 2016

Ok thank you!

@skosno
Copy link

skosno commented Sep 19, 2016

Is there any way to go around this issue? Disabling devtools is an option but would prefer to have them.

@zalmoxisus
Copy link
Owner

I have just published v2.7.0 where you can fix that behaviour by specifying shouldHotReload parameter to false like:

  const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        shouldHotReload: false
      }) : compose;

  const enhancer = composeEnhancers(
    applyMiddleware(...middleware),
    // other store enhancers if any
  );
  const store = createStore(reducer, enhancer);

See the release notes for more details. Feel free to reopen the issue if it doesn't help.

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

3 participants