Open acdlite opened 9 years ago
I dont think the spec should specifically address optimistic updates but being able to express a sequence of actions would allow for someone to implement optimistic updates.
Actions:
[
{ type: 'SAVE_FOO', payload: { fooId: 42, name: 'Foo' }, meta: { id: 'abc1', sequence: 'begin' } },
{ type: 'SAVE_FOO', payload: { fooId: 42, name: 'Foo' }, meta: { id: 'abc1', sequence: 'complete' } },
]
Store or Reducer:
if (action.type == 'SAVE_FOO') {
switch (action.meta.sequence) {
case 'begin':
// Store pending value using action.payload.fooId
// Possibly keep track of request using action.meta.id
break;
case 'complete':
if (isError(action)) {
// Remove pending value, store error letting user know what occurred.
} else {
// Remove and merge pending value.
}
break;
}
}
A user may want a sequence of more then 2 actions, if we borrow slightly from Observable terminology I could see the following values for sequence: begin|next|complete
. An optional sequenceOrder
could be useful as well.
Not sure if this causes any issues but for an action creator that performs some client side validation it might be possible for a complete
sequence to be fired without having a matching begin
.
I like the sequence
idea. Here's how I'd implement it:
{
type,
payload,
meta,
sequence: {
type: 'start', // or 'next', or 'return'
id: 123 // same for every action in the sequence
}
}
I'm going to try this out in redux-promise and redux-actions to see how well it works.
@acdlite It might be useful to have some sequentially increasing number for sequence ordering information. E.g., if progress of 67% arrives after 70%, it would be possible to ignore it.
EDIT: that would be more relevant for Rx, or wherever we need sequence.type === 'next'
, not for optimistic updates.
@acdlite I like the top level sequence
as an object, not a huge fan of return
, would prefer end
or complete
.
@clearjs the sequence order would be useful, I could see it living in meta
or sequence
. Do you think this is something the spec needs to define?
@tappleby It depends. Perhaps, after more use cases are addressed this will be clear.
Generally speaking, the concept of sequence implies ordering. My point was to bring this into discussion, not to suggest any specifics.
If I had to make a decision, I'd introduce ordering information and make it a field of sequence
.
This sounds like a great idea.
With respect to ordering information, I think picking something and sticking with it would keep people from falling into the bikeshedding trap, so I think it'd be good to include in the sequence field.
Now for a little bikeshedding, why not use done
to signal that it finished? Then it would match Iterators. Taking it one step further, why not try to match iterators more closely?
{
type: 'FOO',
payload: { foo: true }
sequence: {
id: 123,
done: false,
value: 'start'
}
}
I like the idea of matching the spec with Iterators :+1: This would also allow you to specify the sequence.value
in any way you choose to. In some cases, the value
could be an object with a progress
key if the user so chooses, or it could just be a plain scalar value if custom objects seem overkill.
It might be too much to try to fit the form of sequence.value
to cover all the possible use cases. That could be left for the application domain to handle so that FSA would only be concerned about the sequence.id
and sequence.done
values. What do you think?
EDIT: P.S. This issue being open is currently the only thing stopping us from using FSA in our application. Some way of resolving this would help us tremendously so that we wouldn't have to invent our own ways of doing optimistic updates.
Matching the Iterators spec is an interesting idea. Part of me does like the idea of being able to identify the start of a sequence without having to track previous sequence ids; this isnt possible using just done
alone. Perhaps this isnt a common use case?
@acdlite how do you feel about the following:
{
type,
payload,
meta,
sequence: {
type: 'start', // or 'next', or 'done'
id: 123, // same for every action in the sequence
value: { progress: 0 } // Optional value
}
}
I feel using done instead of return better describes whats happening and is less likely to be confused with the return keyword.
I am still not sure if value
is necessary, optional data like this could be stored in meta
.
I am still not sure if
value
is necessary, optional data like this could be stored inmeta
.
That would actually be reasonable. If at some point FSA wants to change to add a more thought-out sequence.value
, it would not conflict with ones before.
I agree with @tappleby that return
feels weird in this context. done
seems more appropriate here. :+1:
I like @tappleby's comment: https://github.com/acdlite/flux-standard-action/issues/7#issuecomment-130933371
Also, for someone like me who didn't know about the iterator protocol, here's the link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol
Could someone explain a little more how will be used the iterator approach? What would be the benefits? The actions would need to have an [Symbol.iterator], don't they?
+1 for @tappleby's suggestion.
Hey, random bystander here. Is there consensus about this yet? I'd like to incorporate it into an app we're about to start and basing off FSA's :)
Anything that needs doing, experimenting with, or whatever? Happy to help.
BTW redux-optimist is an interesting approach https://github.com/ForbesLindesay/redux-optimist/
@epeli That's actually very close to what Andrew is doing in redux-promise's sequence
branch
fwiw I made a small todo app using the sequence branch while trying to learn redux and I find it to be pretty nice. https://github.com/eldh/todo
Have no time to look closely yet but this might be of interest: https://github.com/pburtchaell/redux-promise-middleware
I could test the redux-promise "sequence" branch and it worked very well for optimistic updates. I'd love to see this idea implemented into FSA.
I think the proposed sequence solution makes actions less readable. Actions that are part of the same sequence (and not part of start
or done
sequence type) seem more like a stream of progress information, with the most import information hidden away in the sequence key and the payload being unimportant.
I think the proposed sequence solution
There has been many proposed solutions in this issue @rgrwkmn. Which one do you mean?
I'm referring to @tappleby's comment.
As I worked through my own use case example and did some more reading, I've concluded that my criticisms are moot, but since I haven't seen them specifically addressed I'll list them here. Hope this helps:
Actions having sub-actions hidden in the sequence type is less readable. It seems that none of the proposals preclude using different action types, though, so this is a moot point.
The discussion may be missing a distinction between optimistic updates (a standard that could be defined) and other multi-action processes (which could be anything). Semantically, sequence
suggests it can cover any multistep process. A use case relevant to my industry would be deploying a virtual machine. I've described a potential list of rough actions in this gist. This point is also moot because I can see how using sequence
with type
and id
can cover this use case, while I can still use different action types and have the relevant data in the action's payload
.
Yeah so @rgrwkmn for your use case, the only thing that the sequence
value MUST contain is type
and id
. The rest can be inside payload
or meta
, right?
So the spec would look like this:
{
type,
payload,
meta,
sequence: {
type: 'start', // or 'next', or 'done'
id: 123, // same for every action in the sequence
}
}
Which in turn looks very similar to @acdlite comment, only the last value of type
would be done
instead of return
. I have no strong feelings about that, though.
So @acdlite, have you tried your initial spec further? I think we're revolving back around to it, so it seems like a good way to go forward. The spec could always later be amended to include more values, should that be necessary, but at least we could go forward with what you've said.
@valscion :thumbsup:
@acdlite, would you accept a PR implementing the spec I described? Or the spec you have described? What needs to be done would be adding the sequence
key and it's valid keys check to the isFSA
function + amend the README to note optimistic updates, too.
@acdlite should updating the spec here precede finalizing https://github.com/acdlite/redux-actions/tree/sequence ?
I actually have a pending PR for the sequence branch based on discussions in this issue - https://github.com/acdlite/redux-actions/pull/22
@tappleby awesome! IMO it looks good to go. Let me rephrase my question more clearly: since redux-actions
and many other libraries are intended to be FSA compliant, should we make an effort to see the sequence
standard all-the-way through into the spec before implementing it in compliant libraries, or take more of a "draft" approach and implement what we have here to see if it works well before adding it into the standard?
My opinion is that FSA should remain a standard for the data structure for the action only. I think that optimistic updates are best handled at the higher level.
Microcosm has a particularly elegant implementation, where action creators can return ES6 generator functions, which the middleware will continually pull from - facilitating optimistic, or multiple, dispatch from one action.
Anyway, I think that precisely because multiple ways exist to solve the optimistic update problem, it's better not to bake on view onto this into the FSA data structure 'standard'.
What's happening with this? Would love to know if I should wait for this stuff to land, or if I should do a custom implementation...
@DawidLoubser
+1
I think we should just keep FSA the simplest standard and move the async stuffs (include optimistic update) to the actionCreators . And in my opinion, the only things reducer should care about are payload and type, add other attribute on action will making things complex.
Here is my example, a todo list with optimistic update : https://github.com/kpaxqin/redux_example/tree/todomvc
The redux-promise-thunk use thunk to dispatch action in each phase of async promise, and in TodoActions we can use createPromiseThunk directly or compose with another thunk
This is just a demo, but I think it can show the possibility of composing logics in action creator, instead of putting extra things in action object and handle them in reducer.
@acdlite @lukewestby
:-1: for sub-actions inside actions. Having a sequence object with a status inside it would lead to nested switch/case
in reducers. The beauty of FSA is its easy to use it with or without redux-actions, and that ease should be maintained. I really believe sub-actions should have different action types altogether.
:-1: for mixing Async/Promises with Sequence/Generator/Iterator. Those two are different problems. NodeJS made a hacky example of using generators to solve async, but that need not be taken as a standard. We should be looking at async/promise and sequencing as separate issues. Async should be handled separately like how ES7 introduced async/await.
:+1: for keeping FSA simple. As with any standardization process, having this issue in FSA is delaying a trivial but most-hit-upon problem. Request to move the Async/Promise discussion to redux-promise to let things continue forward, and maybe discuss only sequences in the context of FSA.
Request to unstandardize this discussion, and move downstream. Atleast for the Async/Promise handling part.
@acdlite @lukewestby @DawidLoubser @kpaxqin
Opening this issue to track discuss whether FSA should address optimistic updates. If it does, I think it should be as an extension of the core spec.