-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
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
Middleware not dispatched through on init #465
Comments
Both :) function reducer(state = initialState, action) {...} Since
Not sure I understand what the problem is. Could you elaborate? *unless you pass an initial state to |
For context, here's where the init action is dispatched: https://github.com/gaearon/redux/blob/breaking-changes-1.0/src/createStore.js#L140 |
The thing that lead me down this path was just a code-style preference in my reducers. This is a poorly thought out example of the style I was looking to achieve. function status(state = initial, { type, payload }){
const { notification, isError } = payload;
switch(type){
case UPDATE_STATUS:
return { notification, isError };
default:
return state;
}
} As you can probably tell, moving the destructuring from payload to the top of the reducer keeps the switch statements much simpler/cleaner, especially as things start to grow, or assignment is being done, etc. However, this pattern fails because on Redux's I understand that the Another thing that I would probably want in the future is to log the I understand the example I gave that got me to this place could be solved by using a default for the |
Thanks for the additional info. I understand now.
The init action is unique because it's the only dispatch that is called by Redux itself, rather than by the developer (or some extension added by the developer). You were correct in your original comment in saying that this has to do with the functional compositional nature of Redux. When you do a normal dispatch with middleware, you're calling it on a store interface that has been set up like so: const store = applyMiddleware(m1, m2, m3)(createStore)(reducer, initialState); But since the init action is dispatched by the base const store = createStore(reducer, initialState); In a practical sense, this should only a problem if reducers are making hard assumptions about the shape of actions, which probably isn't a good idea. Theoretically, though, it may make more sense to get rid of the init action, and require the use to either 1) pass an initial state to |
@phated even if the I agree with this:
Maybe the question is: is it OK to handle the Re: #376 and according to @acdlite comment above, the only way for the const store = createStore(reducer, initialState)
applyMiddleware(store, m1, m2, m3) Is this correct? |
@acstll it is okay that they are undefined, because they are only used when the action type exists. However, if You are correct that the I am trying to think how the invariant logic would deal with this versus the initial dispatch in createStore. I am wondering if it can be done as a store enhancer, like the middleware. |
that is so true 😄 |
@phated Not sure if you already thought about this, but you could do something like this: (state, { type, payload = {} }) => {
const { notification, isError } = payload;
/* ... */
} This way nothing has to change and it will work just fine with the init action because if |
@johanneslumpe that was noted as the very last sentence of my issue. It isn't about the example that lead me here, it is about the overarching problem. |
Oh sorry @phated I missed that part. Yeah I understand what you are trying to address with this issue, wanted to offer an interim solution - but you already had that one going anyway ;) |
To answer my own question: no, that's not correct. The only way to get that
I'm +1 on this. If you need that |
I don't currently think we're going to do this. There don't seem to be enough upsides, and the minor inconveniences don't seem to me like they're worth changing an API to force the user to dispatch something like If you'd like to continue this discussion please do this in form of a PR (both to source and examples) so we have a specific proposition to talk about. I still think it's unlikely to get merged, but IMO we've ran out of depth discussing it here, and without the code, there isn't much to talk about. |
Meh. The inconsistency of dispatching
We should probably fix it here, even at the cost of a breaking change. Some ideas:
I still wouldn’t want people to put |
The more I think about it, the more I am inclined to a variant that people should provide their own bootstrap action, because even if I managed to implement deferred initialization after enhancer is applied, it still does not guarantee that this is the first dispatched action, because for example Concept of Enhancers unfortunately does not provide any guarantees. Therefore for me, as a library author it does make sense to rely on custom bootstrapping action. Maybe we should advocate this in docs? Speaking about #1240 - this is not directly related because people are trying to solve something which clearly can live in userland (#1240 (comment)) |
Im not a huge fan of using the lazily dispatched Could we take advantage of the fact that store enhancers are now a first class concept (#1294) and ensure the store is initialized w/ the enhancer before the init dispatch action is called? The above change would probably involve deprecating the old method of applying store enhancers. |
Sorry if this is the wrong issue to comment on (hard to keep track with all the inter-dependencies) but here's another vote and use-case for having middleware see the I have a middleware that opens up a WebSocket session, where the sub-protocol allows connecting and disconnecting without dropping the socket. There's some store-dependent logic about whether to clear the session, disconnect codes, etc. that run after function middleware(store) {
const session = new StatefulSocketConnection(...);
processStateChange(session, undefined, state);
return next => action => {
// Pulling out the entire state is unnecessary, but makes processStateChange feel cleaner
const prevState = store.getState();
const ret = next(action);
const state = store.getState();
processStateChange(session, prevState, state);
return ret;
}
} Splitting out |
Here's something interesting that I noticed. If you add this test: it('dispatches an initial action to reducers', () => {
const spy = jest.fn()
const store = createStore((state, action) => spy())
expect(spy.mock.calls.length).toBe(1)
}) It makes sense that it passes. (not sure if its needed, but it is a passing test) it('dispatches an initial action to reducers', () => {
const store = createStore((state = 0, action) => state + 1, 1)
expect(store.getState()).toEqual(1)
}) This test fails because of the init action and the fact that this reducer doesn't care what action is passed to it. I'm not sure what the expected behavior would be here because the reducer is so simple to begin with , maybe it's not relevant at all. But it feels like this test should pass. |
We have a warning in place now to prevent this problem at all: #1485 |
I'm not sure if this has been discussed before (quick search got me no where), but I am noticing that no middleware is executed when redux does its
init
actions. Is this on purpose or just something that has come out of the functional composition nature of redux? I would like to use middleware to ensure payload objects on each of my actions, but theinit
actions cause things to explode due to not passing through middleware.The text was updated successfully, but these errors were encountered: