-
Notifications
You must be signed in to change notification settings - Fork 114
Dispatch in middleware does not use store-enhancer dispatch #36
Comments
I created my store like so: export default function configureStore(initialState = undefined) {
const enhancer = compose(
createStoreWithRouter({
routes,
pathname: location.pathname,
basename: ''
}),
// Middleware
applyMiddleware(
thunkMiddleware,
auth
),
DevTools.instrument()
);
const store = createStore(
reducers,
initialState,
enhancer
); |
Ok, I dug a little more. The problem is that the |
Thanks for reporting this! I'll be investigating this tomorrow 👍 |
Actually, it's even worse than I thought. Middleware should always be the first enhancer and I jsut realised that when fiddling around to get it to work the above is the wrong order. With the correct order of The fix for this isn't that complicated, but it is a bit messy. I've been having a look at the redux documentation and while it doesn't say that you shouldn't dispatch actions from within an enhancer, I'm beginning to think it isn't a good idea because unlike the middleware, it does not start again from the beginning of the chain. The only way I could fix this was to split redux-little-router into two parts. I now have a middleware and a store enhancer. This also meant that I configured the Because middleware is designed with this exact problem in mind, it works. If you are interested in this solution I am happy to write it up or submit a PR. |
Not always true (wish it were that easy!). See where This issue appears to be a pretty serious (and intentional) limitation of Redux: ...which really puts library authors in a tight spot. Older versions of this library required you to install both a store enhancer and a middleware, which definitely hurts usability and expands boilerplate. Going to hack on a solution as soon as I can, would love feedback when I have something ready! |
Also had a thought: in your example, can you try calling |
I can't figure this out for the life of me! 😒 The only solution I know of is to reintroduce the |
Hello, I don't know if this makes sense, bit I would use something else to dispatch a second action when receiving a first one, and that's a Saga from redux-saga (or maybe something from redux-loop if I understand that project correctly). So my question is: are you sure this is a redux-little-router problem? (Still I'm curious too if using |
@tptee Hi, I finally made time to go and do the investigation you asked for. Doing I double checked this by adding this enhancer: const testEnhancer = createStore => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer);
const dispatch = action => {
console.log('testEnhancer dispatch', action);
return store.dispatch(action);
}
return { ...store, dispatch };
}; Which is composed like so: const enhancer = compose(
routerEnhancer,
applyMiddleware(
thunkMiddleware,
auth
),
testEnhancer,
// Browser addon redux tools (develop mode only)
window.devToolsExtension ? window.devToolsExtension() : f => f
); As expected, It is my belief that actions should only be dispatched from middleware. |
The problem happens because with dispatch an action in store enhancer. It didn't follow the We can fix this by separating it to two parts: middleware and store enhancer. Adding the dispatch 'ROUTER_PUSH' to part of middleware should've fixed the problem. |
@philipyoungg Separating the enhancer and middleware does fix the problem as I commented in an earlier post in this thread. |
@dpwrussell @tptee I think I know the problem now. Both have different dispatch method. On if (router) {
router.store.dispatch({
type: replaceState ? REPLACE : PUSH,
payload: location
});
}
}; either replace or push, it will do this (from export default (store, history) => action => {
switch (action.type) {
case PUSH:
history.push(action.payload);
return null;
case REPLACE:
history.replace(action.payload);
return null;
case GO:
history.go(action.payload);
return null;
case GO_BACK:
history.goBack();
return null;
case GO_FORWARD:
history.goForward();
return null;
default:
// We return the result of dispatch here
// to retain compatibility with enhancers
// that return a promise from dispatch.
return store.dispatch(action);
}
}; …which subsquently will dispatch history.listen(newLocation => {
/* istanbul ignore else */
if (newLocation) {
store.dispatch(locationDidChange({
location: newLocation, matchRoute
}));
}
}); the the |
I think I'm going to give up and add the middleware again. I hate that there's no other way to solve this! reduxjs/redux#1702 might make this possible in Redux 4, but it'll also make other things more difficult 😬 |
PR for this: #96 Added a test that confirms that your custom middleware can dispatch router actions! |
How we doing on this? Dispatching in middleware is really important for async actions like ajax form submission handling via |
@joshdcomp There is #96, but I added some comments to it about other aspects of accessing the store which are problematic which you might be interested in also. |
Fixed in |
Hi,
I am experimenting with this:
I have written a barebones middleware for routes with
auth
in theirresult
. Like so:The
setTimeout
is just to simulate an async fetch that I would usually dispatch to the server to see if I had an active session. If not, then I want to go to the login page.This seems to generate a
ROUTER_PUSH
action with a payload of '/login'. The route does not change and a fragment for '/login' is not displayed. However, if I dispatch that same action from anonClick
on myApp
component, the result is aROUTER_LOCATION_CHANGE
with the payload ofpathname
,key
,route
,params
, etc, and it works fine.I am not experienced with redux middleware and suspect that this has something to do with the middleware chain and where
redux-little-router
sits in that chain, but I'm unsure how to address this. Should this be possible?Cheers
The text was updated successfully, but these errors were encountered: