React-admin: Allow optimistic mode exit immediately inside useMutate options.onSuccess callback

Created on 11 Aug 2020  路  12Comments  路  Source: marmelab/react-admin

Is your feature request related to a problem? Please describe.
Hello, I recently played with useMutate hook more extensively. What I noticed that when using options.undoable option we have to provide onSuccess callback to useMutate hook - which is fine. What I had problem with is that I was unable to exit optimistic mode because even though I would dispatch complete action and call undoableEventEmitter.emit('end', { isUndo: false }) my app would always stay in optimistic mode.

Describe the solution you'd like
After some diggiging into source code i traced everything to this two lines of code. Problem is that undoableEventEmitter.once handler is registered after calling onSuccess callback. Which means that if your callback is sync and executes immediately undoableEventEmitter.emit will never register for that specific mutate call.

Describe alternatives you've considered
I solved the problem by simply adding minimal timeout to my onSuccess callback:

    onSuccess: () => {
        setTimeout(() => {
            undoableEventEmitter.emit('end', { isUndo: false })
        }, 100)
    }

Whit this undoableEventEmitter.oncegets registered and after 100ms the emit call is handled and app exits optimistic mode.

Solution
I think that onSuccess should be called after undoableEventEmitter.once de facto switching above mentioned two lines of code. This would allow to use optimistic mode and presenting (for example) updated UI to the user and then immediately exiting optimistic mode which would then do the API call.

I am open to implementing this in a pull request if needed.

enhancement

Most helpful comment

3rd option profits from optimistic mode but also immediately does network call so that data can be synced on backend - no wait is needed from some undo timeout.

And this again goes back to my initial comment, react-admin already supports this but there is a small logic misstep that if fixed would allow for no delay at all.

All 12 comments

Hi and thanks for the issue. Can you please describe what you are trying to achieve? A concrete example would be nice.

I have a button that unfavorites certain node from the custom list. I wish to use optimistic mode through options.undoable to immediately show UI change (user unfavorited node) and then exectute real API call by exiting optimistic mode.

This is defacto standard feature for react-admin but i noticed that because of the logic issue I mentioned above you need to have some timeout after which you can exit optimistic mode.

I don't understand your use case. If you want to allow the end user to undo their latest action, you can't fetch the dataProvider until the undo delay expires, otherwise the UI will display inconsistent data. Optimistic actions update the local state to show what the user expects immediately, but the server data isn't updated until the delay expires.

To avoid such inconsistencies, if you fetch data as a success side effect of an optimistic action, react-admin actually delays that call until the optimistic delay expires.

If you want to fetch the dataProvider immediately, you can't use an optimistic action.

you can't fetch the dataProvider until the undo delay expires

This is exactly my point. If I do not pass undo dalay or it is 0 the onSuccescallback will never trigger because of the ordering at this two lines of code.

By registering undoableEventEmitter before calling onSuccess callback this would be possible.

Not all optimistic actions need to be undoable in my opinion, so this small change would enable just that.

I still don't understand your use case. Why don't you pass undoable=false to bypass the undoable delay?

undoable=false will then disable optimistic mode completely, no?

Yes. But since I don't understand what you want, it seems like the right solution :D

If I understand correctly, @capJavert want the UI to optimistically update but to not allow users to undo. To not wait for the API call to resolves before updating the UI

As @djhi said.

I wish to be able to use optimistic mode without delay or undoable.

As far as I know only way to trigger optimistic mode through dataProvider calls is to pass options.undoable=true which then triggers optimistic mode and waits for end call (which usually comes from undoable handler) to resume network calls.

Real world example:
You have a PUT request that takes a long time, but you are 99% (pretty pretty sure) it will succeed because of your frontend validation. You don't want to block user until network call so we push changes immediately.

Everything I described is already standard for react-admin, as its UI is optimistic (through undo) but write/update actions (PUT, PATCH) could easily work optimistically without undo.

OK, let me rephrase that.

Currently, we offer 2 modes:

  1. pessimistic mode, which waits for the server to respond to trigger side effects. For long-running actions, this isn't ideal
  2. optimistic mode, which triggers side effects immediately and delays the actual call unless the user chooses to undo

You want to offer a 3rd mode:

  1. optimistic mode without undo, which triggers side effects and the actual call immediately

I have yet to understand how that 3rd mode is better / more desireable than the second mode. In your real-world example, what forbids you from using normal optimistic mode?

3rd option profits from optimistic mode but also immediately does network call so that data can be synced on backend - no wait is needed from some undo timeout.

And this again goes back to my initial comment, react-admin already supports this but there is a small logic misstep that if fixed would allow for no delay at all.

using undoableEventEmitter.emit('end', { isUndo: false }) in onSuccess does not work entirely :
If the request to the server fail, the modification is not cancelled anymore.
For now, what I did to have immediate optimistic without undo is dispatching a CRUD_GET_LIST_SUCCESS (in my case I needed to update in a list) with the updated data before sending the request normally with the useUpdate hook.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marknelissen picture marknelissen  路  3Comments

fzaninotto picture fzaninotto  路  3Comments

Dragomir-Ivanov picture Dragomir-Ivanov  路  3Comments

mbj36 picture mbj36  路  3Comments

alukito picture alukito  路  3Comments