Comments (17)
Thanks for reporting this! I'll be investigating this tomorrow π
from redux-little-router.
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 π¬
from redux-little-router.
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
);
from redux-little-router.
Ok, I dug a little more. The problem is that the store.dispatch
in the middleware does not use the store enhancer's dispatch
function. So actions dispatched in middleware will go direct to the store
's dispatch
method which explains why it works for other things, but not PUSH
etc. react-little-router's enhancer would have intercepted PUSH
if it was in the chain.
from redux-little-router.
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 applyMiddleware
then createStoreWithRouter
, the middleware will never receive LOCATION_CHANGED
events at all.
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 history
, basename
and matcher
in my configureStore
function as these are passed into both the middleware and enhancer.
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.
from redux-little-router.
Middleware should always be the first enhancer
Not always true (wish it were that easy!). See where redux-loop
ran into this same issue:
redux-loop/redux-loop#28
redux-loop/redux-loop#39
This issue appears to be a pretty serious (and intentional) limitation of Redux:
reduxjs/redux#1240
reduxjs/redux#1051
...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!
from redux-little-router.
Also had a thought: in your example, can you try calling next
instead of store.dispatch
just to see what happens?
from redux-little-router.
I can't figure this out for the life of me! π The only solution I know of is to reintroduce the routerMiddleware
, but as an optional piece to allow dispatching router events in middleware.
from redux-little-router.
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 next()
instead of dispatch()
in @dpwrussell 's code could work. @tptee which one do you use from your enhancer?)
from redux-little-router.
@tptee Hi, I finally made time to go and do the investigation you asked for.
Doing next
instead of store.dispatch
does not work. I wasn't really expecting it to, because the order that the enhancers are dispatched is left-to-right as they are in the compose
statement.
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, testEnhancer
logs the next(action)
call that I make from my middleware. If the testEnhancer is moved before applyMiddleware
in the composition, then it is not.
It is my belief that actions should only be dispatched from middleware.
from redux-little-router.
The problem happens because with dispatch an action in store enhancer. It didn't follow the apply middleware
generated dispatch action.
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.
from redux-little-router.
@philipyoungg Separating the enhancer and middleware does fix the problem as I commented in an earlier post in this thread.
from redux-little-router.
@dpwrussell @tptee I think I know the problem now.
I've read the codes and realized that there are 2 store: The first one is router store
, and the second one is store that generated from applyMiddlewares.
Both have different dispatch method.
On link.js
, there's this happens when we dispatch a path payload
if (router) {
router.store.dispatch({
type: replaceState ? REPLACE : PUSH,
payload: location
});
}
};
either replace or push, it will do this (from wrap-dispatch
)
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 ROUTE_LOCATION_CHANGED
type on the correct store. That's why ROUTER_PUSH
never logged as an action, but ROUTE_LOCATION_CHANGE
logged correctly from middleware.
history.listen(newLocation => {
/* istanbul ignore else */
if (newLocation) {
store.dispatch(locationDidChange({
location: newLocation, matchRoute
}));
}
});
the hacky
solution is to export router.store.dispatch
API to be accessible from redux-little-router
, not only inherited from React Context
the non hacky
solution is to rewrite the library with only one dispatch action: ROUTE_LOCATION_CHANGED
from redux-little-router.
PR for this: #96
Added a test that confirms that your custom middleware can dispatch router actions!
from redux-little-router.
How we doing on this? Dispatching in middleware is really important for async actions like ajax form submission handling via redux-saga
from redux-little-router.
@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.
from redux-little-router.
Fixed in v11.0.0
! Huzzah!
from redux-little-router.
Related Issues (20)
- Warning in build with 14.3 HOT 1
- [MAJOR] Move `immutable/*` re-exports to `immutable/index.js` HOT 1
- Examples give flow errors, (Flowtypes incorrect?) HOT 1
- No underscores in query params. HOT 1
- Trouble using authentication
- Parameter basename in function routerForBrowser does not work if it ends with a slash HOT 1
- There is not enough information in the README HOT 1
- Publish TypeScript types along with package? HOT 4
- Routes ending in a slash are broken
- Infra: Automatically verify index.d.ts HOT 1
- How to test (context troubles) HOT 1
- Link's active state is super opinionated HOT 1
- How to match child route and parent sibling <Fragment>s simultaneously HOT 2
- Missing pathname Flow annotation
- TypeScript: Redundant confliting definition of History interface HOT 3
- Hot module reloading causes issues? HOT 1
- Url changes, but component doesn't change on push. Correct component shows up on refresh with correct url. HOT 1
- Hash router?
- How to render an overlay on top of main content (2 fragments at once)? HOT 2
- Support redux v4 HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from redux-little-router.