Skip to content

Commit

Permalink
feat: stabilize hydrate side effect with nullable parameter (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoryConrad committed Jan 7, 2024
1 parent e540cc4 commit 06e3c79
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 49 deletions.
43 changes: 0 additions & 43 deletions packages/rearch/lib/experimental.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,47 +30,4 @@ extension ExperimentalSideEffects on SideEffectRegistrar {
return (() => state, (T newState) => state = newState);
});
}

/// A mechanism to persist changes made to some provided state.
/// Unlike [persist], [hydrate] allows you to pass in the state to persist,
/// if there is one, to enable easier composition with other side effects.
///
/// Defines a way to interact with a storage provider of your choice
/// through the [read] and [write] parameters.
/// - If [newData] is [Some], then [newData] will be persisted and
/// overwrite any existing persisted data.
/// - If [newData] is [None], then no changes will be made to the currently
/// persisted value (for when you don't have state to persist yet).
///
/// [read] is only called once; it is assumed that if [write] is successful,
/// then calling [read] again would reflect the new state that we already
/// have access to. Thus, calling [read] again is skipped as an optimization.
// NOTE: experimental because I am not sold on the Option<T> parameter.
AsyncValue<T> hydrate<T>(
Option<T> newData, {
required Future<T> Function() read,
required Future<void> Function(T) write,
}) {
final readFuture = use.callonce(read);
final readState = use.future(readFuture);
final (getPrevData, setPrevData) =
use.rawValueWrapper<Option<T>>(None<T>.new);
final (getWriteFuture, setWriteFuture) =
use.rawValueWrapper<Future<T>?>(() => null);

if (newData case Some(value: final data)) {
final dataChanged =
getPrevData().map((prevData) => data != prevData).unwrapOr(true);
if (dataChanged) {
setPrevData(Some(data));
setWriteFuture(() async {
await write(data);
return data;
}());
}
}

final writeState = use.nullableFuture(getWriteFuture());
return (writeState ?? readState).fillInPreviousData(readState.data);
}
}
37 changes: 37 additions & 0 deletions packages/rearch/lib/src/side_effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,43 @@ extension BuiltinSideEffects on SideEffectRegistrar {
return (state, persist);
}

/// A mechanism to persist changes made to some provided state.
/// Unlike [persist], [hydrate] allows you to pass in the state to persist,
/// if there is one, to enable easier composition with other side effects.
///
/// Defines a way to interact with a storage provider of your choice
/// through the [read] and [write] parameters.
/// - If [newData] is [Some], then [newData] will be persisted and
/// overwrite any existing persisted data.
/// - If [newData] is [None], then no changes will be made to the currently
/// persisted value (for when you don't have state to persist yet).
///
/// [read] is only called once; it is assumed that if [write] is successful,
/// then calling [read] again would reflect the new state that we already
/// have access to. Thus, calling [read] again is skipped as an optimization.
AsyncValue<T> hydrate<T>(
T? newData, {
required Future<T> Function() read,
required Future<void> Function(T) write,
}) {
final readFuture = use.callonce(read);
final readState = use.future(readFuture);
final (getPrevData, setPrevData) = use.rawValueWrapper<T?>(() => null);
final (getWriteFuture, setWriteFuture) =
use.rawValueWrapper<Future<T>?>(() => null);

if (newData != null && newData != getPrevData()) {
setPrevData(newData);
setWriteFuture(() async {
await write(newData);
return newData;
}());
}

final writeState = use.nullableFuture(getWriteFuture());
return (writeState ?? readState).fillInPreviousData(readState.data);
}

/// Allows you to trigger and watch [Future]s
/// (called mutations, since they often mutate some state)
/// from within the build function.
Expand Down
7 changes: 1 addition & 6 deletions packages/rearch/test/side_effects_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:rearch/experimental.dart';
import 'package:rearch/rearch.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -61,11 +60,7 @@ void main() {
CapsuleHandle use,
) {
final (state, setState) = use.state<int?>(null);
final hydrateState = use.hydrate(
state != null ? Some(state) : const None<int>(),
read: read,
write: write,
);
final hydrateState = use.hydrate(state, read: read, write: write);
return (hydrateState, setState);
}

Expand Down

0 comments on commit 06e3c79

Please sign in to comment.