-
Notifications
You must be signed in to change notification settings - Fork 3.5k
waitFor leads to wrong design #209
Comments
Are you suggesting that you would duplicate the logic and the cached value in multiple stores? That sounds like a maintenance nightmare. I vastly prefer a single source of truth. Even if you have a global cache, with something like baobab, you would still potentially have one mutation that needs to maybe happen first, depending on logic, and then a second mutation that happens based on that value. So we have something like this:
Not sure how you'd pull that off without |
Yes, I am suggesting to duplicate state. It does not necessarily mean that I need to duplicate logic. For the shared logic we can use stateless service (in Domain Driven Design terms). It's essential to realize the frequency of such a case (state dependency between stores). It's definitely a very minor use case and therefore I can imagine using shared stateless service as an ideal solution. However, what does concern me a lot are tight dependencies between stores. For example the snippet you have posted above, let's imagine the application grows and there is a new action (
|
Yes, responding to the same action in both stores is how we handle this at Facebook. The only problem we have with this approach is that an engineer may not realize they need to respond to a particular action in a "downstream" store. We've talked about and experimented with developing warnings whenever an upstream store changes in an action that is not handled by its downstream stores. If you have a solution where the state and logic are not duplicated, we could consider suggesting this in the documentation. I'll take a look at stateless services. |
Uhm... I'm going to jump in because this is interesting. I've been using Flux in Meteor and after a while I realised I wasn't using waitFor at all. That's because in Meteor you can use reactive variables like this: // StoreA:
var bar = new ReactiveVar("default value");
StoreA.getBar = function(){
return bar;
}
// ...
// then, within the switch
case FOO:
if (shouldBarBeMutated()) {
mutateBar();
// you don't need emitChange with Meteor's reactivity either
}
break;
// StoreB, not in the switch
Tracker.autorun(function(){
mutateBaz(StoreA.getBar());
// not need for emitChange either, UI's update reactively
}); StoreA.getBar() returns a reactive variable, Actually, when it comes to case QUX, it still works without any other code in StoreB: // StoreA, withing the switch:
case QUX:
mutateBar(); Because again, whenever On other side, Optimizely's Flux implementation don't use waitFor either, because they use getters outside stores (point 3 of how Nuclear differs from Flux). They can do that because state is not spread out through stores, but contained in a single immutable map. |
Thanks for those explanations, Luis. I think this is somewhat similar to where a few folks I've talked to are going with Flux + Baobab. I'm very interested in the idea of a global state cache, accessible by all stores. It does seems to reduce the amount of dependency logic, but I'm a bit concerned about the amount of hidden magic going on in a system where updates cascade through the data layer. This is exactly the situation that Flux was created to avoid. Making the updates explicitly declared in the stores helped us to reason about complex updates that touched a lot of domains. When mutations are happening reactively (I might say magically) it becomes more difficult to understand how data is changing throughout the application. But perhaps it doesn't matter. Maybe all we need is to focus on writing in an FRP style throughout the stores, and I might be able to get on board with this. I'll take a deeper look at the suggestions you've offered. |
Yeah, I definitely think that reasonable option would be to keep the store stateless and pull the state to single shared storage. The storage of shared state may be for example Baobab or any other immutable tree data structure. That would mean stores would become just a dummy action handlers (business logic handlers) and I am still thinking about the name, as Store is probably not very descriptive in this case. Bill, you have mentioned that even in this case, you most likely won't avoid the need for Anyway, I would say that the need to use Even though Baobab is a great idea, there is in my opinion one even better approach. We can possibly have just one emit event for entire tree (any change in the tree would result in the emit change) and the topmost view would listen to the change and pass the state down through the component hierarchy via props. However, there is one requirement. All components but the root must be pure. If the condition is met and we are using immutable data structure (which we should anyway) we could possibly have |
There is my contribution on this topic. You can treat Dispatcher's API as a low-level and create your definition of stores above. For example, in my Stores implementation I've used That means that you can just use getter and don't even think about |
Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed. |
I think at this point Redux captures the wait-for-less use case very cleanly. I would highly recommend that library. We will not be removing waitFor from our implementation of dispatcher any time soon though. |
I don't really think it has any benefit to keep
waitFor
method in dispatcher as it's leading to wrong design.A great example of that is implementation in flux-chat. I was actually thinking about sending a PR where
waitFor
method is not used, then I realised that the only correct way would be to get rid ofThreadStore
andUnreadThreadStore
.I believe the right approach would be to to keep entire state in one store and get rid of direct dependencies which may eventually lead to inconsistent state as the application grows. For example, once new action which modifies
currentID
is presented and handled only inThreadStore
.If we need a state variable across multiple stores, it's much better to listen to all actions which modify the state variable instead of presenting dependencies between stores.
waitFor
does not have any other utilisation than waiting for stores to handle actions so that we can be sure that the state has been set already and we don't need to do that as long as we keep the variable in every single store that needs the variable.The text was updated successfully, but these errors were encountered: