Skip to content
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

Debounce (protect against accidental multiple operations) #75

Open
subzero911 opened this issue Jun 13, 2020 · 4 comments
Open

Debounce (protect against accidental multiple operations) #75

subzero911 opened this issue Jun 13, 2020 · 4 comments
Labels
enhancement New feature or request

Comments

@subzero911
Copy link

subzero911 commented Jun 13, 2020

How to implement protection against too often heavy operations?
For instance, if I pressed the "Save" button quickly several times. I don't want to save multiple times.
I want to wait a 500 ms after last press and save only once.

In MobX we have a ready-made reaction 'delay' parameter (check the 101-105 lines):
https://github.com/brianegan/flutter_architecture_samples/blob/master/mobx/lib/stores/todo_store.dart

In a pure Provider architecture I'd use an RxDart debounce() to control saving:

class Store with ChangeNotifier {
final _saveController = StreamController<item>(); //helper stream
final List<Item> _list = []; //data

Store() { _saveController.stream.debounce(ms: 500).listen => // do your save here  }

 void add(item) { //action
 _list.add(item);
 notifyListeners();
  _saveController.add(item); //add event
 }
}

But I have no idea, how to create similar functionality with Async Redux, because it's not recommended to keep streams in the store.

@subzero911 subzero911 changed the title Debounce (protect against multiple operations) Debounce (protect against accidental multiple operations) Jun 13, 2020
@marcglasberg
Copy link
Owner

Please, first read this section of the documentation: https://pub.dev/packages/async_redux#progress-indicators

There are many ways to do that:

  1. You can use the Wait class in the store to create a modal barrier (in the UI) to prevent the button from being tapped, or to turn off the button's onTap callback, while the save is being performed.

  2. Actions have an abortDispatch method. You can use the Wait class in the store to abort the dispatch while the save is being performed (or just return null from the reducer).

  3. You can add a static bool isSaving field to the action. Make it true when the action starts (using the action's before method), and make it false when it finishes (using the action's after method). Then abort the dispatch if the bool is already true. Note the abortDispatch method runs before the before method.

  4. If you just want to have a time-based debouncing that cancels actions before some elapsed time, add a static static DateTime last field to the action. Save the action's stateTimestamp to this variable when the action starts (using the action's before method), and make it null when it finishes (using the action's after method). Then abort the dispatch if the action's stateTimestamp is not at least x seconds from saved timestamp.

  5. However, if you want to have a time-based debouncing that groups actions within some elapsed time, it's a bit more complex. The store itself does this when it needs to call the state persistor. But it's also more complex because it checks not only the elapsed time, but also if the previous save has finished. I guess I could add some more functionality to the AsyncRedux's Wait class to allow for these more complex deboucings, but I'm not sure it's necessary. Usually I just prevent the button from being tapped during the save process, in the UI, using the Wait class, and that's usually better than a time-based debouncing.

@subzero911
Copy link
Author

subzero911 commented Jun 16, 2020

Usually I just prevent the button from being tapped during the save process, in the UI, using the Wait class, and that's usually better than a time-based debouncing.

It was just an example with button.
I could move a slider back and forth and save after 2 seconds when I stopped. I cannot block UI in this case.

@marcglasberg
Copy link
Owner

Yes, as I said, the store does that with the persistor. I will see if I can expose this functionality for general use.

@marcglasberg marcglasberg added the enhancement New feature or request label Jun 19, 2020
@jans-y
Copy link

jans-y commented Mar 10, 2021

For that purpose, I am using this library:

https://pub.dev/packages/easy_debounce

I am wrapping all dispatch() calls that I need to debounce with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants