-
Notifications
You must be signed in to change notification settings - Fork 7
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
Async actions #1
Comments
Hey Ivan, thanks! Completely synchronous means that the API does not have asynchronous methods. Actions can therefore not handle returned Promises. That was a design decision. You are maybe familiar with flummox where actions can return Promises. The Issue with that is that stores that handle the (async) actions need to take care of the whole flow and intermediate states. With flummox you would register an async action by defining a With minimal-flux you would do something like this: class Messages extends Actions {
fetch() {
this.wait();
Axios.get("/some/api").then(this.complete).catch(this.fail);
}
wait() {
this.emit("wait");
}
complete(data) {
this.emit("complete", data);
}
fail(err) {
this.emit("fail", err);
}
} This is probably a bit more code than just returning a Promise, but you have more control over the flow. You maybe want to inform other parts of your application that you are waiting for a request. Inside the wait() {
// Inside of actions you have access to all other actions that were registered.
// Maybe there are multiple requests going on and you want show the user a progress bar.
this.actions.loader.increment();
this.emit('wait');
}
complete(data) {
this.actions.loader.decrement();
this.emit('complete', data);
}
fail(err) {
this.actions.loader.decrement();
// If something goes wrong, you maybe want to log it:
this.actions.logger.add(err);
this.emit('fail');
} You can find another example here Hope that helps :) |
Thanks. Yes, I'm familiar with Flummox. I've been trying about all flux implementations out there :) Also your flux == dispatcher. That's good, less concepts to confuse. You're trying to keep visible and obvious the fact that actions are just event emitters. It feels cleaner in flummox to just return value or promise instead of emitting them. But they have problems with invoking subsequent actions during the handle of original one (acdlite/flummox#82). Maybe their approach is the straight source of the issue. Anyway, those string constants (emitted event names) are very easy to put wrong and this will require debugging (cause no syntax error). That's a pain point. Reflux made the same choice of explicit trigger but with Stores. And this caused a lot of problems.
Here are my store handlers in flummox: loadManyCompleted(models) {
this.setState({
models: this.state.models.merge(models),
loaded: true,
});
}
loadManyFailed(response) {
this.setState({
loaded: true,
})
}
loadOneCompleted(model) {
this.setState({
models: this.state.models.set(model.get("id"), model),
});
} There is no request handling, just response handling. That looks ok, are you disagree? And here are my actions. Very clean and readable: async loadMany() {
let response = await Axios.get(`/api/robots/`);
return Map([for (model of response.data) [model.id, Map(model)]]);
}
async loadOne(id) {
let response = await Axios.get(`/api/robots/${id}`);
return Map(response.data);
}
async remove(id) {
let response = await Axios.delete(`/api/robotz/${id}`, {id});
return id;
} |
Yes you're right. The emitting part can cause confusion. I'm currently adding some warnings when actions emit events that are not registered as actions. But that shouldn't be the solution. I had another idea for solving this problem: complete(data) {
// Every action could be bound to an own object that provides
// a convenient `dispatch` method. Then you could write:
this.dispatch(data);
} Under the hood this would call Thanks for your feedback! |
I've updated my prev comment with a note about Reflux. I just think that Store and Action should have about the same depth of code layer in this stuff. Now stores use Magic is always about benefits and drawbacks. If it frees us from thinking and errors – that's good. |
Yes, agree, the code is super clean. But as you pointed out in acdlite/flummox#82 you cannot invoke subsequent actions. Besides that I think there are some problems with invoking actions from stores. This will break the unidirectional data flow, which makes it difficult to reason about changes in state:
What do you mean by that?
|
Same as that (quoting myself):
|
Well I don't really think we have unidirectional data-flow in Flux. We have loop. I may easily be wrong, but how to add alert on failed load without subsequent action?
Yeah, but they somehow messed it up. For example, in Reflux we can use them for sure. |
Ah, ok got it :) Yes, maybe the |
Yes, right, kind of a unidirectional loop :) With minimal-flux you could do that by having your alert store listening to any kind of fail action. class Messages extends Actions {
fetch() {
Axios.get("/some/api").then(this.complete).catch(this.fail);
}
fail(err) {
this.emit("fail", err);
}
// ...
}
class AlertsStore extends Store {
constructor() {
this.handleAction('messages.fail', this.addAlert);
}
// ...
} That's why I think returning from actions is not the right way. |
And that makes class AlertsStore extends Store {
constructor() {
this.handleAction('users.loadManyFailed', this.add);
this.handleAction('articles.loadManyFailed', this.add);
this.handleAction('whatever.loadManyFailed', this.add);
...
}
// ...
} Well, maybe the right way is just to accept it and move along. I'm not sure. |
Well it turns out more interesting than it seems from the first sight. We can approach this in two ways. InteractiveAlertActions < UserStore [calls AlertActions.add()]
AlertActions < DocumentStore [calls AlertActions.add()]
AlertActions < SubscriptionStore [calls AlertActions.add()] ReactiveUserActions < AlertStore [listens to UserActions.loadManyFailed]
DocumentActions < AlertStore [listens to DocumentActions.loadManyFailed]
SubscriptionActions < AlertStore [listens to SubscriptionActions.loadManyFailed] Replacing with letters for brevity and removing "Store" & "Actions" suffixes that don't matter...: InteractiveA < U
A < D
A < S
ReactiveU < A
D < A
S < A
I bet there's more info I can't extract right now... React on itself is not 100% reactive, but if we believe the more reactivity - the better, I think |
I'm not sure what does "Completely synchronous" on the title page means.
How about async actions?
P.S.
Current API looks cute, I like it.
The text was updated successfully, but these errors were encountered: