Nommez ce que votre application peut faire
Remplacez les callbacks dispersés par des actions typées et une fonction de mise à jour centrale, afin que chaque changement reste facile à suivre.
Résultat : les boutons et un contrôle de sélection envoient des intentions typées vers une fonction update prévisible.
01
Suivez une action du widget jusqu'à l'état
Les pièces sont volontairement petites. Ensemble, elles rendent chaque intention explicite et regroupent les mutations au même endroit.
1. Enregistrez le type d'action
Un marqueur copiable implémente ActionSchema et relie l'application à son enum d'actions lors de la compilation.
2. Décrivez les intentions
CounterAction nomme Increment, Reset et SelectPace au lieu de cacher ces choix dans des callbacks sans lien.
3. Émettez depuis les contrôles
Button::on_click envoie une variante. SegmentedControl::on_change transforme la valeur choisie en une autre variante.
4. Mettez à jour au même endroit
AppBuilder::update reçoit l'état modifiable, les services modifiables et une action, puis renvoie les éventuelles Commands suivantes.
CounterActionupdate(...)State02
Construisez le parcours d'une action typée
Construisez les actions typées en trois passages au lieu d'absorber toute l'application d'un seul coup.
Construisez par petites étapes
Chaque bloc de code change une seule idée de l'application de référence vérifiée.
Étape 1 : nommer les intentions
L'enum liste chaque événement accepté par l'exemple et ActionSchema indique à l'application quel enum acheminer.
#[derive(Clone)]
enum CounterAction {
Increment,
Reset,
SelectPace(Pace),
}
#[derive(Clone, Copy)]
struct CounterActions;
impl ActionSchema for CounterActions {
type Action = CounterAction;
}Étape 2 : centraliser les décisions
update reçoit une action, modifie le State concerné et renvoie les éventuelles Commands suivantes.
fn update(
state: &mut CounterState,
_services: &mut (),
action: CounterAction,
) -> Commands<CounterAction> {
match action {
CounterAction::Increment => state.count.update(|value| *value += 1),
CounterAction::Reset => state.count.set(0),
CounterAction::SelectPace(pace) => state.pace.set(pace),
}
Commands::none()
}Étape 3 : laisser les contrôles émettre
Le contrôle segmenté et les boutons proposent des actions. Ils ne modifient pas eux-mêmes l'état applicatif.
SegmentedControl::new()
.option(Pace::Calm, "Calm")
.option(Pace::Focused, "Focused")
.selected(view_state.pace.clone())
.on_change(CounterAction::SelectPace),
)
.child(
Row::new()
.gap(12.0)
.child(
Button::with_label("Add one")
.on_click(CounterAction::Increment),
)
.child(
Button::with_label_variant("Reset", ButtonVariant::Secondary)
.on_click(CounterAction::Reset),
),Compilé avec Rust 1.88 sur beta.2. ActionSchema, AppBuilder::state, actions, update, Commands, Button et SegmentedControl sont publics.
03
Pourquoi l'état contient des handles State
AppBuilder possède l'état ordinaire de l'application. L'exemple y conserve des handles State clonables afin que la fonction de mise à jour centrale et le contenu de la fenêtre partagent les mêmes valeurs visibles.
Commands::none garde un sens
La fonction de mise à jour n'a ici aucune action différée ou suivante à planifier. La modification des handles State avertit déjà leurs lecteurs retenus.
À vous de jouer
Exercice : ajoutez une intention typée
Gardez toutes les modifications du compteur dans la fonction centrale update.
- 1Ajoutez Decrement à l'enum des actions et traitez-le avec saturating_sub dans update.
- 2Reliez un bouton secondaire à cette nouvelle action.
Le bouton émet seulement Decrement. Il ne doit ni recevoir ni cloner le State du compteur.
Le nouveau bouton modifie le compteur, zéro ne déborde jamais et toutes les modifications vivent encore dans update.