# Managing state

> No reactive binding — state in fields/ViewModel, the View translates state→components by hand.

Source: https://crossfly.dev/docs/learn/managing-state — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 7. Managing state

> **What you'll learn:** CrossFly's state model — where people coming from Flutter/React need to
> **unlearn**. **Prerequisites:** [Handling input](/docs/learn/handling-input).

## Complete example (copy, paste, and run)
A lightweight ViewModel (`TarefaVM`) with a manual `observe(Runnable)` (there's no reactive binding): the View re-populates
the components when the state changes. A button adds tasks and the list re-renders. Compiles as is.
```java
package com.exemplo;

import java.util.*;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

public class MainWindow extends Window
{
   private final TarefaVM vm = new TarefaVM();
   private final Component listaBox = new Component();
   private int contador = 1;

   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Button add = new Button("Adicionar tarefa");
      add.onAction(c -> vm.adicionar(new Tarefa("Tarefa " + (contador++))));
      add(LAY_AUTO, add);

      listaBox.setLayout(new VerticalLayout());
      add(LAY_FILL, listaBox);

      vm.observe(this::renderizarLista);   // re-populates when the state changes
      renderizarLista();
   }

   private void renderizarLista()
   {
      listaBox.removeAll();
      for (Tarefa t : vm.tarefas) listaBox.add(LAY_AUTO, new Label(t.titulo));
      listaBox.setValidLayout(false);      // the size changed
      Component.repaint();
   }
}

class Tarefa
{
   final String titulo;
   Tarefa(String titulo) { this.titulo = titulo; }
}

class TarefaVM
{
   private Runnable onChange;
   public List<Tarefa> tarefas = new ArrayList<>();

   public void observe(Runnable r) { this.onChange = r; }
   private void mudou() { if (onChange != null) onChange.run(); }

   public void adicionar(Tarefa t)
   {
      var novo = new ArrayList<>(tarefas); novo.add(t);
      tarefas = novo;
      mudou();                             // notifies the View (on the paint thread)
   }
}
```
> The snippets below show **only the core** (what goes inside `onShow()`). To run them, put them in the `MainWindow` skeleton above.

## There's no reactive binding
Changing the model does **not** update the UI on its own. State lives in fields (or in a lightweight **ViewModel**), and the
View **translates state → components manually**, calling `repaint()` (and `setValidLayout(false)` if the
size changed).

## A lightweight ViewModel with `observe`
```java
public class TarefaVM {
   private Runnable onChange;
   public List<Tarefa> tarefas = List.of();
   public void observe(Runnable r) { this.onChange = r; }
   private void mudou() { if (onChange != null) onChange.run(); }

   public void adicionar(Tarefa t) {
      var novo = new ArrayList<>(tarefas); novo.add(t);
      tarefas = novo;
      mudou();                    // notifies the View (on the paint thread)
   }
}
```

## The View reacts
```java
@Override public void onShow() throws Exception {
   super.onShow();
   // … builds UI …
   vm.observe(this::renderizarLista);   // re-populates the components when the state changes
   vm.carregar();                       // triggers the load (in background — next chapter)
}

private void renderizarLista() {
   listaBox.removeAll();
   for (Tarefa t : vm.tarefas) { CardTarefa card = new CardTarefa(); listaBox.add(LAY_AUTO, card); card.bind(t); }
   listaBox.setValidLayout(false);
   Component.repaint();
}
```
> Rule: mutations and notifications **always on the paint thread**. If the data came from the background, enter
> `Component.onUI(...)` before touching the state the View reads (see the next chapter).

## What you build in this step
The **`TarefaVM`** with the screen re-rendering when the state changes.

## Recap
No reactive: state in fields/ViewModel; the View translates state→components with `repaint()`; a manual
`observe(Runnable)` replaces the binding.

## Next steps
→ [Loading data](/docs/learn/loading-data)

## See also
- Concept: [State and data flow](/docs/concepts/state-and-data-flow) · [App architecture](/docs/best-practices/arquitetura-de-app)
