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

add support to configure conditional listening #18

Merged
merged 1 commit into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions state_beacon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.17.1

- Make conditional listening configurable for `Beacon.createEffect` ,`Beacon.derived` and `Beacon.derivedFuture`

## 0.17.0

- Mdd debugLabel to beacons
Expand Down
9 changes: 5 additions & 4 deletions state_beacon/lib/src/effect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ final Set<EffectClosure> _listenersToPingAfterBatchJob = {};
class _Effect {
final Set<Listeners> dependencies;
late final EffectClosure func;
final bool _supportConditional;

_Effect() : dependencies = <Listeners>{};
_Effect(this._supportConditional) : dependencies = <Listeners>{};

VoidCallback execute(Function fn) {
func = EffectClosure(() {
_cleanup(this);
if (_supportConditional) _cleanup(this);
_effectStack.add(this);
try {
fn();
Expand All @@ -41,8 +42,8 @@ class _Effect {
}
}

VoidCallback effect(Function fn) {
final effect = _Effect();
VoidCallback effect(Function fn, {bool supportConditional = true}) {
final effect = _Effect(supportConditional);
return effect.execute(fn);
}

Expand Down
47 changes: 35 additions & 12 deletions state_beacon/lib/src/state_beacon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ abstract class Beacon {
///
/// If `manualStart` is `true`, the future will not execute until [start()] is called.
///
/// If `supportConditional` is `true`, the effect look for its dependencies on its first run.
/// This means once a beacon is added as a dependency, it will not be removed even if it's no longer used.
/// Defaults to `true`.
///
/// Example:
/// ```dart
/// final age = Beacon.writable<int>(18);
Expand All @@ -376,16 +380,20 @@ abstract class Beacon {
T Function() compute, {
bool manualStart = false,
String? debugLabel,
bool supportConditional = true,
}) {
final beacon = DerivedBeacon<T>(manualStart: manualStart)
..setDebugLabel(debugLabel ?? 'DerivedBeacon<$T>');

final unsub = effect(() {
// beacon is manually triggered if in idle state
if (beacon.status.value == DerivedStatus.idle) return;
final unsub = effect(
() {
// beacon is manually triggered if in idle state
if (beacon.status.value == DerivedStatus.idle) return;

beacon.forceSetValue(compute());
});
beacon.forceSetValue(compute());
},
supportConditional: supportConditional,
);

beacon.$setInternalEffectUnsubscriber(unsub);

Expand All @@ -401,6 +409,10 @@ abstract class Beacon {
/// If `cancelRunning` is `true`, the results of a current execution will be discarded
/// if another execution is triggered before the current one finishes.
///
/// If `supportConditional` is `true`, the effect look for its dependencies on its first run.
/// This means once a beacon is added as a dependency, it will not be removed even if it's no longer used.
/// Defaults to `true`.
///
/// Example:
/// ```dart
/// final counter = Beacon.writable(0);
Expand Down Expand Up @@ -430,19 +442,23 @@ abstract class Beacon {
bool manualStart = false,
bool cancelRunning = true,
String? debugLabel,
bool supportConditional = true,
}) {
final beacon = DerivedFutureBeacon<T>(
compute,
manualStart: manualStart,
cancelRunning: cancelRunning,
)..setDebugLabel(debugLabel ?? 'DerivedFutureBeacon<$T>');

final unsub = effect(() async {
// beacon is manually triggered if in idle state
if (beacon.status.value == DerivedFutureStatus.idle) return;
final unsub = effect(
() async {
// beacon is manually triggered if in idle state
if (beacon.status.value == DerivedFutureStatus.idle) return;

await beacon.run();
});
await beacon.run();
},
supportConditional: supportConditional,
);

beacon.$setInternalEffectUnsubscriber(unsub);

Expand Down Expand Up @@ -493,6 +509,10 @@ abstract class Beacon {
/// Creates an effect based on a provided function. The provided function will be called
/// whenever one of its dependencies change.
///
/// If `supportConditional` is `true`, the effect look for its dependencies on its first run.
/// This means once a beacon is added as a dependency, it will not be removed even if it's no longer used.
/// Defaults to `true`.
///
/// Example:
/// ```dart
/// final age = Beacon.writable(15);
Expand All @@ -509,8 +529,11 @@ abstract class Beacon {
///
/// age.value = 20; // Outputs: "You can vote!"
/// ```
static VoidCallback createEffect(Function fn) {
return effect(fn);
static VoidCallback createEffect(
Function fn, {
bool supportConditional = true,
}) {
return effect(fn, supportConditional: supportConditional);
}

/// Executes a batched update which allows multiple updates to be batched into a single update.
Expand Down
2 changes: 1 addition & 1 deletion state_beacon/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: state_beacon
description: A reactive primitive and simple state managerment solution for dart and flutter
version: 0.17.0
version: 0.17.1
repository: https://github.com/jinyus/dart_beacon

environment:
Expand Down
34 changes: 34 additions & 0 deletions state_beacon/test/src/effect_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ void main() {
expect(called, equals(6));
});

test('should continue listening to unused beacons', () {
final name = Beacon.writable("Bob");
final age = Beacon.writable(20);
final college = Beacon.writable("MIT");

var called = 0;
Beacon.createEffect(
() {
called++;
// ignore: unused_local_variable
var msg = '${name.value} is ${age.value} years old';

if (age.value > 21) {
msg += ' and can go to ${college.value}';
}

// print(msg);
},
supportConditional: false,
);

name.value = "Alice";
age.value = 21;
college.value = "Stanford";
age.value = 22;
college.value = "Harvard";
age.value = 18;

// Should still listen to college beacon even if age is less than 21
college.value = "Yale";

expect(called, equals(7));
});

test('should run when a dependency changes', () {
var beacon = Beacon.writable<int>(10);
var effectCalled = false;
Expand Down