LogoSignals.dart
Copy Markdown
rodydavis/signals.dart 999999

Effect

Represents a passive observer that runs arbitrary side-effect code in response to signal changes.

Represents a passive observer that runs arbitrary side-effect code in response to signal changes.

An Effect tracks which signals are accessed within its callback function, and automatically schedules itself to re-run whenever those dependencies change.

Under the hood, the reactivity engine tracks reads on .value inside the active effect block. Once the block completes, a subscription is registered for each accessed signal. When any of those signals mutate, the effect is added to the microtask queue and executed synchronously during the next tick.

Do not modify a tracked signal *directly* inside an effect callback, as this will trigger another execution of the same effect, causing an infinite loop (cycle) and throwing a cycle detection error. To read a signal non-reactively, use .peek().

Example Usage#

1. Standard Side-Effect

import 'package:preact_signals/preact_signals.dart';

final count = signal(0);

void main() {
  // Creates and immediately starts the effect
  final logger = Effect(() {
    print('Active count is: ${count.value}');
  });

  count.value = 1; // Prints: "Active count is: 1"
  logger.dispose();
}

2. Effect Cleanup Callback

If your effect returns a function, that function is registered as a cleanup callback. The cleanup callback is executed right before the next effect run, or when the effect is disposed. This is highly useful for cleaning up timers, controllers, or other subscriptions:

final query = signal('search_term');

final searchEffect = Effect(() {
  final currentQuery = query.value;
  print('Initiating search for: $currentQuery');

  final timer = Timer(Duration(milliseconds: 500), () {
    print('Search completed for: $currentQuery');
  });

  // Return cleanup callback
  return () {
    print('Cancelling previous search timer');
    timer.cancel();
  };
});

Constructors#

View Constructors
Effect(this.fn, {String? name, EffectOptions? options})

Creates a new Effect instance with the passive side-effect callback fn.

You can optionally provide:

  • A name for debugging/observer tracing.
final effectObj = Effect(() => print(count.value), name: 'count_logger');

Properties#

View Properties
Function()? fn

@internal The effect callback.

int globalId
Function? cleanup

@internal The cleanup callback.

Effect? nextBatchedEffect

@internal The next effect in the batched effects queue.

int flags
String? name

The name of the effect for debugging.

Methods#

View Methods
void callback()

@internal Executes the callback function and schedules cleanups.

void Function() start()

@internal Starts tracking dependency subscriptions.

void notify()
void dispose()

Disposes of the effect, stopping future callback executions, executing any registered cleanup routines, and unsubscribing from all dependency signals.

void Function() call()

Activates/Runs the effect immediately.

Returns a bound disposer function that can be called to stop the effect.

void cleanupEffect()

@internal Runs the user-defined cleanup callback if registered.

void disposeEffect()

@internal Disposes resources held by the effect.

void endEffect(Listenable? prevContext)

@internal Concludes the current effect evaluation round and restores the evaluation context context.


effect#

Creates and immediately executes a new reactive Effect.

Returns a bound disposer function that can be called to stop the effect and unsubscribe it from all tracked signals.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final count = signal(0);
final dispose = effect(() {
  print('Count is: ${count.value}');
  return () => print('Cleaning up!');
});

void main() {
  count.value = 10; // Prints: "Cleaning up!" then "Count is: 10"
  dispose(); // Stops the effect and unsubscribes
}