From ea659078dfb52546b65d976fd9378df5b0150216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Le=C3=B3n?= Date: Mon, 20 May 2024 18:20:10 -0600 Subject: [PATCH] doc: Add note about lazy state to `Signal`, `UseState`, `UseAsyncState` and `UseReducer`. --- website/src/content/docs/classes/signal.mdx | 22 +++- .../content/docs/hooks/use_async_state.mdx | 118 ++++++++++++++---- .../src/content/docs/hooks/use_reducer.mdx | 32 ++++- website/src/content/docs/hooks/use_state.mdx | 32 +++-- 4 files changed, 161 insertions(+), 43 deletions(-) diff --git a/website/src/content/docs/classes/signal.mdx b/website/src/content/docs/classes/signal.mdx index 927a023b..7f8a827b 100644 --- a/website/src/content/docs/classes/signal.mdx +++ b/website/src/content/docs/classes/signal.mdx @@ -8,8 +8,9 @@ sidebar: import { HM, HT } from '@/components/Highlight/index.ts'; import MDXRedry from '@/components/MDXRedry.astro'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; -import * as ListeningChangesHook from '@/content/docs/shareds/listening_changes_hook.mdx'; +import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; `Signal` is reactive state that encapsulate a `value` changing over time. When the `value` of a signal changes, it automatically notifies its observers. @@ -42,6 +43,23 @@ final strSignal = Signal("initial value"); final userSignal = Signal(User()); ``` + + +```dart title="counter_controller.dart" collapse={1-3, 8-10} "late" "Reactter.lazyState" "Signal" +class CounterController { + final int initialCount; + + late final count = Reactter.lazyState( + () => Signal(this.initialCount), + this + ); + + Counter([this.initialCount = 0]); +} +``` + + + ### Reading and writing the value `Signal` has a `value` property that allows to read and write its state: @@ -87,7 +105,7 @@ userSignal.refresh(); ### Listening to changes - diff --git a/website/src/content/docs/hooks/use_async_state.mdx b/website/src/content/docs/hooks/use_async_state.mdx index 8d5f942c..b2f4e26b 100644 --- a/website/src/content/docs/hooks/use_async_state.mdx +++ b/website/src/content/docs/hooks/use_async_state.mdx @@ -7,14 +7,15 @@ sidebar: import { HE, HK, HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; -import * as ListeningChangesHook from '@/content/docs/shareds/listening_changes_hook.mdx'; +import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; `UseAsyncState` is a [hook](/reactter/core_concepts/hooks) that allows to declare state variables and manipulate its `value` asynchronously, which in turn notifies about its changes to listeners. ## Syntax -```dart +```dart showLineNumbers=false UseAsyncState( T initialValue, Future asyncFunction(), @@ -46,14 +47,14 @@ UseAsyncState.withArg( - `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: - ```dart + ```dart showLineNumbers=false FutureOr resolve(); // for UseAsyncState.withArg FutureOr resolve(A arg); ``` - `when`: A method that allows to computed a value depending on its status. - Syntax: - ```dart + ```dart showLineNumbers=false R? when({ WhenValueReturn? standby, WhenValueReturn? loading, @@ -75,7 +76,7 @@ UseAsyncState.withArg( `UseAsyncState` can be initialized using the constructor class: -```dart "UseAsyncState" +```dart showLineNumbers=false "UseAsyncState" "asyncFunction" final uAsyncState = UseAsyncState('Initial value', asyncFunction); Future asyncFunction() async { @@ -84,12 +85,36 @@ Future asyncFunction() async { } ``` + + +```dart title="user_controller.dart" collapse={1-3, 8-18} "late" "Reactter.lazyState" "UseAsyncState" +class UserController { + final String userId; + + late final uUser = Reactter.lazyState( + () => UseAsyncState.withArg(null, this.getUser), + this, + ); + + UserController(required this.userId) { + uUser.resolve(this.userId); + } + + Future getUser(String userId) async { + await Future.delayed(Duration(seconds: 2)); + return User(id: userId, name: 'John Doe'); + } +} +``` + + + ### 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: -```dart +```dart showLineNumbers=false ".value" ".resolve" print("${uAsyncState.value}"); // Initial value await uAsyncState.resolve(); print("${uAsyncState.value}"); // Resolved value @@ -99,11 +124,11 @@ print("${uAsyncState.value}"); // Resolved value stateName: 'UseAsyncState', }}/> -### Usage with argument +### Using with argument `UseAsyncState` can be used with arguments by using the `withArg` constructor: -```dart +```dart showLineNumbers=false "UseAsyncState.withArg" "asyncFunctionWithArg" final uAsyncStateWithArg = UseAsyncState.withArg( 'Initial value', asyncFunctionWithArg @@ -115,42 +140,67 @@ Future asyncFunctionWithArg(int arg) async { } ``` -### Resolving with argument & reading the value - -`UseAsyncState` has a `resolve` method that updates the state asynchronously by calling the `asyncFunctionWithArg` function. +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: -```dart -print("Current state: ${uAsyncStateWithArg.value}"); // Initial value +```dart showLineNumbers=false ".value" ".resolve" +print("${uAsyncStateWithArg.value}"); // Initial value await uAsyncStateWithArg.resolve(10); -print("Current state: ${uAsyncStateWithArg.value}"); // Resolved value with arg: 10 +print("${uAsyncStateWithArg.value}"); // Resolved value with arg: 10 ``` -### Updating the value +If you want to add more arguments, you can supply it using the `Record`(if your proyect support) +or [`Args`](/reactter/classes/args)(A generic arguments provided by Reactter), e.g.: -Use `update` method to notify changes after run a set of instructions: +```dart showLineNumbers=false "UseAsyncState.withArg" "asyncFunctionWithArgs" ".resolve" ".value" +final uAsyncStateWithArgs = UseAsyncState.withArg>( + 'Initial value', + asyncFunctionWithArgs +); -```dart -uAsyncState.update((value) { - uAsyncState.value = "New value"; -}); -``` +Future asyncFunctionWithArgs(ArgsX3 args) async { + await Future.delayed(Duration(seconds: 2)); + return "Resolved value with args: ${args.arg}, ${args.arg2}, ${args.arg3}"; +} -Use `refresh` method to force to notify changes. +print("${uAsyncStateWithArgs.value}"); // Initial value +await uAsyncStateWithArgs.resolve(ArgsX3('arg1', 'arg2', 'arg3')); +print("${uAsyncStateWithArgs.value}"); // Resolved value with args: arg1, arg2, arg3 +``` -```dart -uAsyncState.refresh(); +### 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`](/reactter/classes/memo) to cache the resolving `value`, e.g.: + +```dart "UseAsyncState" "Memo.inline" +final translateState = UseAsyncState.withArg>( + null, + /// `Memo` stores the value resolved in cache, + /// and retrieving that same value from the cache the next time + /// it's needed instead of resolving it again. + Memo.inline( + (ArgsX3 args) async { + final text = args.arg; + final from = args.arg2; + final to = args.arg3; + // this is fake code, which simulates a request to API + return await api.translate(text, from, to); + }, + AsyncMemoSafe(), // avoid to save in cache when throw a error + ), +); ``` + ### Using `when` method `UseAsyncState` provides a `when` method that allows to computed a value depending on its status: -```dart +```dart showLineNumbers=false ".when" final result = uAsyncState.when( standby: (value) => "Standby", loading: (value) => "Loading", @@ -183,9 +233,25 @@ Builder( Learn more about [Rendering Control](/reactter/core_concepts/rendering_control) in Reactter. ::: +### Updating the value + +Use `update` method to notify changes after run a set of instructions: + +```dart showLineNumbers=false ".update" +uAsyncState.update((value) { + uAsyncState.value = "New value"; +}); +``` + +Use `refresh` method to force to notify changes. + +```dart showLineNumbers=false ".refresh" +uAsyncState.refresh(); +``` + ### Listening to changes - diff --git a/website/src/content/docs/hooks/use_reducer.mdx b/website/src/content/docs/hooks/use_reducer.mdx index 5aec02d5..b78d806b 100644 --- a/website/src/content/docs/hooks/use_reducer.mdx +++ b/website/src/content/docs/hooks/use_reducer.mdx @@ -7,8 +7,9 @@ import { Code } from "@astrojs/starlight/components"; import MDXRedry from '@/components/MDXRedry.astro'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; import UseReducerExample from '@/examples/use_reducer_example.dart.code?raw'; +import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; -import * as ListeningChangesHook from '@/content/docs/shareds/listening_changes_hook.mdx'; +import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; export const mark = ["UserAction", "UseReducer", "ReactterAction", /([^u]User)/g, "reducer", ".dispatch", ".value"]; @@ -53,6 +54,33 @@ UseReducer( + + + +```dart title="user_controller.dart" collapse={1-3, 8-19} "late" "Reactter.lazyState" "UseReducer" +class UserController { + final String userId; + + late final uUser = Reactter.lazyState( + () => UseReducer.withArg(this.reducer, null), + this, + ); + + UserController(required this.userId); + + User? reducer(User? state, ReactterAction action) { + if (action is IncrementedEgeUserAction) { + return User(name: state!.name, age: state.age + 1); + } else if (action is ChangedNameUserAction) { + return User(name: action.name, age: state!.age); + } + return state; + } +} +``` + + + ### Writting the action The action is a class that inherits from the `ReactterAction` class. @@ -139,7 +167,7 @@ userState.refresh(); ### Listening to changes - \ No newline at end of file diff --git a/website/src/content/docs/hooks/use_state.mdx b/website/src/content/docs/hooks/use_state.mdx index 8325f97e..0282b2b7 100644 --- a/website/src/content/docs/hooks/use_state.mdx +++ b/website/src/content/docs/hooks/use_state.mdx @@ -4,11 +4,13 @@ description: Learn how to use the `useState` hook in Reactter. sidebar: order: 1 --- +import { Code } from "@astrojs/starlight/components"; import { HK, HM, HT } from '@/components/Highlight'; import MDXRedry from '@/components/MDXRedry.astro'; import StateMethodsLite from '@/content/docs/shareds/state_methods_lite.mdx'; +import * as NoteLateState from '@/content/docs/shareds/note_late_state.mdx'; import * as NoteStateValueNotifies from '@/content/docs/shareds/note_state_value_notifies.mdx'; -import * as ListeningChangesHook from '@/content/docs/shareds/listening_changes_hook.mdx'; +import * as ListeningChangesState from '@/content/docs/shareds/listening_changes_state.mdx'; `UseState` is a [hook](/reactter/core_concepts/hooks) that allows to declare state variables and manipulate its `value`, which in turn notifies about its changes to listeners. @@ -40,19 +42,23 @@ final intState = UseState(0); final strState = UseState("initial value"); final userState = UseState(User()); ``` -:::note -To appropriately use the `late` hook inside a class registered via the [dependency injection](/reactter/core_concepts/dependency_injection), use `Reactter.lazyState`. -Learn more about it [here](/reactter/methods/to_manage_state/lazy_state). e.g: - ```dart title="Counter.dart" "Reactter.lazyState" "UseState" - class Counter { - final int initialCount; - late final count = Reactter.lazyState(() => UseState(initialCount), this); + + +```dart title="counter_controller.dart" collapse={1-3, 8-10} "late" "Reactter.lazyState" "UseState" +class CounterController { + final int initialCount; - Counter([this.initialCount = 0]); - } - ``` -::: + late final count = Reactter.lazyState( + () => UseState(this.initialCount), + this, + ); + + Counter([this.initialCount = 0]); +} +``` + + ### Reading and writing the value @@ -84,7 +90,7 @@ userState.refresh(); ### Listening to changes - \ No newline at end of file