async_redux
async_redux copied to clipboard
Debounce (protect against accidental multiple operations)
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.
Please, first read this section of the documentation: https://pub.dev/packages/async_redux#progress-indicators
There are many ways to do that:
-
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'sonTap
callback, while the save is being performed. -
Actions have an
abortDispatch
method. You can use theWait
class in the store to abort the dispatch while the save is being performed (or just returnnull
from the reducer). -
You can add a
static bool isSaving
field to the action. Make ittrue
when the action starts (using the action'sbefore
method), and make itfalse
when it finishes (using the action'safter
method). Then abort the dispatch if the bool is alreadytrue
. Note theabortDispatch
method runs before thebefore
method. -
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'sstateTimestamp
to this variable when the action starts (using the action'sbefore
method), and make itnull
when it finishes (using the action'safter
method). Then abort the dispatch if the action'sstateTimestamp
is not at leastx
seconds from saved timestamp. -
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 theWait
class, and that's usually better than a time-based debouncing.
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.
Yes, as I said, the store does that with the persistor. I will see if I can expose this functionality for general use.
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.