-
-
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
Stateless dispatcher #119
Stateless dispatcher #119
Conversation
Just curious: any reason to prefer destructuring of a single argument vs 2 separate arguments? It does introduce the overhead of creating a new object each time. Yes, adding new arguments is less likely to break things, but is that the only motivation here? |
Yep, it's entirely to avoid an API change. And it only gets called once, not on each dispatch. (But even if it did, the performance impact would be infinitesimal compared to everything else that goes on during a dispatch cycle.) |
@acdlite yeah I'm sure you're right about that. Just trying to play the devil's advocate a bit. I think the more we question general, the better or more "prove" the final solution will be :) |
@johanneslumpe Of course :) |
We do have to call |
By default it's just the identity function. I can't think of any use case where it would be very computationally heavy. The more I think about it, though, I don't like the current implementation in this PR. |
The most important part of this PR for me is figuring out where the state lives. Based on the discussion in #113, I think it makes most sense for the state to always live in a single place, the Redux instance. The dispatcher should have no local state, and use If we can agree on that, everything else is just a matter of finding the correct API. |
I most certainly agree with that sir! After all, we are striving to only have a single source of truth. The less scattered the state is - even though it might just be references to the same object - the better. Regarding the debugger api: the example in #113 does have the need to wrap the dispatcher directly though. And in order to log actions properly there does not seem to be a different way than hooking into it directly? |
Yes, it should hook into it directly, but my concern is what the top-level API looks like for users. Right now it's |
Anyway, gotta go to bed, but I'll come back to this tomorrow. |
Agree
It does make sense yes. The |
In fact in that example I'm using the default dispatcher. I'm not wrapping it. Instead, I'm wrapping the store. And my wrapper puts all actions (and intermediate states) into the main state into a “hidden” field. So there's no need for a custom dispatcher (or wrapping the dispatcher) to implement time travel I think. (I might be wrong though :-). It's enough to store the history inside the main state, and to subscribe to that history from a React component. |
I'm going to take a look at this in a few days. Thank you! Very busy atm :-/ |
Please correct me if I'm wrong: My only concern with this is the addition of |
Hmm, I'm a bit confused why. Can you please elaborate? How is |
I guess I phrased that poorly. If you're storing meta information on the state atom, and the state lives entirely on the Redux instance, then |
In other words, a single function cannot express both |
Is it a bad idea to have a convention that the application's state lives inside a field called |
Here's something else. I want to be able to subscribe to the hidden state updates from the monitoring components: @connect(state => ({
entries: state[StateKeys.LOG]
}))
export default class ReduxMonitor { Am I asking for too much at this point? |
@gaearon Haha, no that's a very reasonable thing to want to do. Maybe...
|
I like parts 2) and 3) but I'm still in favor of using a |
@gaearon Here's my proposal: // Create redux only accepts a hash of stores, not a dispatcher
createRedux(stores)
// For advanced usage, like dispatchers and middleware, expose raw Redux class
// I know, "ew, gross, classes"
// IMO it's preferable to overloading `createRedux()`
class Redux({ dispatcher, initialState, prepareState })
// Extensions, like dev tools, should provide factories a la `createRedux()`
// that wrap around the Redux class. E.g.
createRecorderRedux(stores) |
@acdlite I see where you are going, but I think the way @gaearon implemented the recorder (wrapping the store) is much simpler and intuitive. The only issue is exposing meta state in Adding more API weight with prepareState, Redux class, getRawState, createRecorderRedux, etc. just to create a hidden recorder, seems like a bad trade off. Maybe you have other uses for it? I haven't found any need to manipulate/hide the main state like prepareState allows. |
@taylorhakes The main point of this PR is to make the dispatcher stateless so we can prevent bugs like this one: #110. I'm totally fine dropping the |
@acdlite Definitely. Didn't mean to imply anything about the stateless dispatcher part. I think that is a great idea. |
@taylorhakes Cool, glad we're on the same page :) |
Separate instances are starting to appeal to me. (Although I'm not sure about how exactly we want to arrange their interaction.) This also plays well with time travel monitor component wanting to subscribe to its own "state". If we have separate instances, it will just work within its own Provider. There will also be no need for something like prepareState. I'll think about it some more and probably post another API proposal.. |
Can we at least move forward with the making the dispatcher stateless? I don't really care about the prepareState part at all. On Sun, Jun 21, 2015 at 5:34 PM, Dan Abramov [email protected]
|
Yeah I understand, I just don't want to change internal API twice in a few days :-). I think I'm ready to draft the new internal API based on #113 proof of concept I have locally (will demo at the conf). Give me a little more time! |
Closing in favor of #166 |
Superseded by #195. |
This PR mainly does two things:
setState()
by the dispatcher.createRedux()
acceptsprepareState()
as the third parameter*, which is used to prepare the atom before being sent to subscribers. Akin to a globalselectState()
, but with a different name to avoid confusion (e.g.prepareState()
does not have to return a plain object).*I don't like this as a final API, but we should discuss this more before introducing a breaking change to
createRedux()
.These two changes allow custom dispatchers to store "meta" state on the state atom without exposing it to consumers. #113
It also changes the signature of the dispatcher (by which I mean the higher-order function that creates
dispatchFn()
— we really need to work on / document some of this terminology) fromdispatcher(initialState, setState)
todispatcher({ getState, setState })
. It does not includedispatch
as suggested here, since that can be implemented inside the dispatcher instead.Speaking of which, theI'll submit a separate PR for this, since it's not really related.createDispatcher()
API has been changed slightly toThoughts? I will update with better
createRedux()
API once we come to a consensus.