Skip to content

UseAsyncState

UseAsyncState is a hook that allows to declare state variables and manipulate its value asynchronously, which in turn notifies about its changes to listeners.

Syntax

UseAsyncState<T>(
T initialValue,
Future<T> asyncFunction(),
);
// UseAsyncState with arguments
UseAsyncState<T, A>.withArg(
T initialValue,
Future<T> asyncFunction(A arg),
);

UseAsyncState accepts these arguments:

  • initialValue: Initial value of T type that it will hold.
  • asyncFunction : A function that returns a Future<T> to update the state asynchronously. This function is called by the resolve method and sets the value property.

Properties & Methods

UseAsyncState provides the following properties and methods:

  • value: A getter that allows to read its state.
  • status: A getter that allows to read its status. It can be:
    • UseAsyncStateStatus.standby: Represents the standby status, indicating that the state is idle.
    • UseAsyncStateStatus.loading: Denotes the loading status, indicating that an asynchronous operation is in progress.
    • UseAsyncStateStatus.done: Indicates that the asynchronous operation has been successfully completed.
    • UseAsyncStateStatus.error: Signifies that an error has occurred during the asynchronous operation.
  • error: A getter that allows to get the error object when the asyncFunction fails.
  • resolve : A method that updates the state asynchronously by calling the asyncFunction function.
    • Syntax:
      FutureOr<T?> resolve();
      // for UseAsyncState.withArg
      FutureOr<T?> resolve(A arg);
  • when : A method that allows to computed a value depending on its status.
    • Syntax:
      R? when<R>({
      WhenValueReturn<T, R>? standby,
      WhenValueReturn<T, R>? loading,
      WhenValueReturn<T, R>? done,
      WhenErrorReturn<R>? error,
      });
    • Arguments:
      • standby : A function that returns a value when the state is UseAsyncStateStatus.standby.
      • loading : A function that returns a value when the state is UseAsyncStateStatus.loading.
      • done : A function that returns a value when the state is UseAsyncStateStatus.done.
      • error : A function that returns a value when the state is UseAsyncStateStatus.error.
  • Methods inherited from RtState (Learn more here):

    • update : A method to notify changes after run a set of instructions.
    • refresh : A method to force to notify changes.
    • * bind : A method to bind an instance to it.
    • * unbind : A method to unbind an instance to it.
    • * dispose : A method to remove all listeners and free resources.

Usage

Declaration

UseAsyncState can be initialized using the constructor class:

final uAsyncState = UseAsyncState<String>('Initial value', asyncFunction);
Future<String> asyncFunction() async {
await Future.delayed(Duration(seconds: 2));
return "Resolved value";
}

Resolving & reading the state

UseAsyncState has a resolve method that updates the state asynchronously by calling the asyncFunction function. After resolving the state, you can read the value, like this:

print("${uAsyncState.value}"); // Initial value
await uAsyncState.resolve();
print("${uAsyncState.value}"); // Resolved value

Using with argument

UseAsyncState can be used with arguments using the withArg constructor:

final uAsyncStateWithArg = UseAsyncState.withArg<String, int>(
'Initial value',
asyncFunctionWithArg
);
Future<String> asyncFunctionWithArg(int arg) async {
await Future.delayed(Duration(seconds: 2));
return "Resolved value with arg: $arg";
}

To resolve the state with an argument, you can use the resolve method with the argument. After resolving the state, you can read the value, like this:

print("${uAsyncStateWithArg.value}"); // Initial value
await uAsyncStateWithArg.resolve(10);
print("${uAsyncStateWithArg.value}"); // Resolved value with arg: 10

If you want to add more arguments, you can supply it using the Record (if your proyect support) or Args (A generic arguments provided by Reactter), e.g.:

final uAsyncStateWithArgs = UseAsyncState.withArg<String, ArgsX3<String>>(
'Initial value',
asyncFunctionWithArgs
);
Future<String> asyncFunctionWithArgs(ArgsX3<String> args) async {
await Future.delayed(Duration(seconds: 2));
return "Resolved value with args: ${args.arg}, ${args.arg2}, ${args.arg3}";
}
print("${uAsyncStateWithArgs.value}"); // Initial value
await uAsyncStateWithArgs.resolve(ArgsX3('arg1', 'arg2', 'arg3'));
print("${uAsyncStateWithArgs.value}"); // Resolved value with args: arg1, arg2, arg3

Using with Memo

UseAsyncState does not cache the resolving value, meaning that it will resolve the value every time resolve is called, potentially impacting performance, especially if the asyncFunction is expensive. In this case, you should consider using  Memo  to cache the resolving value, e.g.:

1
final translateState = UseAsyncState.withArg<String?, ArgsX3<String>>(
2
null,
3
/// `Memo` stores the value resolved in cache,
4
/// and retrieving that same value from the cache the next time
5
/// it's needed instead of resolving it again.
6
Memo.inline(
7
(ArgsX3<String> args) async {
8
final text = args.arg;
9
final from = args.arg2;
10
final to = args.arg3;
11
// this is fake code, which simulates a request to API
12
return await api.translate(text, from, to);
13
},
14
AsyncMemoSafe(), // avoid to save in cache when throw a error
15
),
16
);

Using when method

UseAsyncState provides a when method that allows to computed a value depending on its status:

final result = uAsyncState.when(
standby: (value) => "Standby",
loading: (value) => "Loading",
done: (value) => "Done",
error: (error) => "Error: $error",
);
print("Result: $result");

Updating the value

Use update method to notify changes after run a set of instructions:

uAsyncState.update((value) {
uAsyncState.value = "New value";
});

Use refresh method to force to notify changes.

uAsyncState.refresh();

Listening to changes

When value has changed, the UseAsyncState will emit the following events(learn about it here):

  • Lifecycle.willUpdate event is triggered before the value change or update , refresh methods have been invoked.
  • Lifecycle.didUpdate event is triggered after the value change or update , refresh methods have been invoked.

Example of listening to changes:

1
Rt.on(
2
myAsyncState,
3
Lifecycle.didUpdate,
4
(_, state) => print("State value has changed to: ${state.value}"),
5
);