Skip to content

State Management

State management is a critical aspect of any application. It allows you to manage the state of your application, and facilitates seamless tracking and handling of changes to it.

Reactter provides a variety of mechanisms for state management, including classes, hooks, and methods:

How it works

Reactter’s state management system is based on the concept of reactivity. Contrary to the prevailing notion that implementing reactive programming in Dart can be challenging, Reactter greatly simplifies this process. To dive into the concept, let’s start by exploring what constitutes a state in Reactter.

State

All state in Reactter are classes that inherit ReactterState , which encapsulates the data and behavior of a particular state, and provides a way to notify observers when the state changes.

Reactter offers two fundamental approaches for creating states: Signal and Hooks .

State methods

ReactterState class provides some methods for managing states, which are:

  • update : Executes a callback function and notify its listeners that the state has changed. When it is invoked, it emits two events to signal the state transition: Lifecycle.willUpdate is emitted first, indicating the impending update, followed by Lifecycle.didUpdate once the update process is complete.
  • refresh : Forces the state to notify its listeners that it has changed. Unlike update , it emits only the Lifecycle.didUpdate event, as it doesn’t involve any preparatory steps before the notification.
  • bind : Establishes a connection between the state and a specific instance. This connection allows the instance to reactively update based on changes to the state. By binding the state, the instance becomes aware of changes to the state and can appropriately reflect those changes in its behavior.
  • unbind : Releases the connection between the state and the instance. When unbinding, the instance will no longer receive updates from the state. This can be useful when an instance is no longer actively using the state or when it needs to detach from the state temporarily or permanently.
  • dispose : Is responsible for cleaning up the state and any associated listeners or resources. Disposing of the state ensures that it is properly released and no longer consumes memory or processing resources unnecessarily.

Example

Let’s see an example of how a Signal state is used and what happens under the hood.

main.dart
1
import 'dart:async';
2
import 'package:reactter/reactter.dart';
3
4
// Create a reactive state called `count` using the `Signal` class
5
final count = Signal(10);
6
7
void main() async {
8
// Listen to the `didUpdate` event of the `count` state
9
// and print the `value` of `count` each time it changes
10
Reactter.on(
11
count,
12
Lifecycle.didUpdate,
13
(_, __) => print('Count: $count')
14
);
15
16
// Create a timer that decrements the `value` of `count`
17
// by 1 every second until it reaches 0
18
await Timer.periodic(Duration(seconds: 1), countdown);
19
}
20
21
// Decrement the `value` of `count` by 1 each time the timer ticks
22
// and cancel the `timer` when the `value` of `count` reaches 0
23
void countdown(Timer timer) {
24
count.value -= 1;
25
26
if (count.value == 0) {
27
timer.cancel();
28
}
29
}

During the process, as the value of count changes and triggers the Lifecycle.didUpdate event, internally within the Signal class, the update method is invoked to notify its listeners(in line 11 of the code below), as follows:

signal.dart
1
class Signal<T> extends ReactterState[...] {
2
T _value;
3
4
Signal(this._value);
5
6
T get value => _value;
7
8
set value(T val) {
9
if (_value == val) return;
10
11
update((_) => _value = val);
12
}
13
14
[...]
15
}