Aprenda já a criar estado derivado em Java com o Megalodonte
Aprenda já a criar estado derivado em Java com o Megalodonte
Se você já viu o post anterior sobre o app contador, sabe que o State<T> do Megalodonte é o bloco básico de tudo: você muda o valor, a UI atualiza sozinha. Mas e quando você precisa de um valor que depende de outro estado? É aí que entra o ComputedState.
Neste exemplo prático, construí um app bem simples: um campo de texto onde o usuário digita algo, e logo abaixo aparece o tamanho (quantidade de caracteres) daquele texto, atualizado em tempo real a cada tecla pressionada.
O problema
Normalmente, em UIs imperativas, você precisaria:
- Escutar o evento de mudança do campo de texto;
- Pegar o novo valor;
- Calcular o tamanho;
- Atualizar manualmente o componente de texto na tela.
Com estado reativo, esses 4 passos somem. Você declara a relação entre os dados, e o framework cuida da propagação.
O código
Main.java — ponto de entrada padrão, define o tema e registra o listener de fechamento da aplicação:
package my_app;
import megalodonte.ListenerManager;
import megalodonte.application.MegalodonteApp;
import megalodonte.base.theme.ThemeManager;
import megalodonte.theme.DefaultTheme;
public class Main {
static void main() {
ThemeManager.setTheme(new DefaultTheme());
MegalodonteApp.run(context -> context.useView(new HomeScreen()), ev->{
if(ev == MegalodonteApp.Event.CloseRequest){
System.out.println("Clicked on X - close application");
ListenerManager.disposeAll();
}
});
}
}
HomeScreen.java — onde a mágica do ComputedState acontece:
package my_app;
import megalodonte.ComputedState;
import megalodonte.base.components.Component;
import megalodonte.base.components.ScreenComponent;
import megalodonte.base.state.State;
import megalodonte.components.Text;
import megalodonte.components.inputs.Input;
import megalodonte.components.layout_components.Container;
import megalodonte.props.ContainerProps;
public class HomeScreen implements ScreenComponent {
State<String> textState = new State<>("");
ComputedState<String> textLenghtComputed = ComputedState.of(
()-> "Size is: " + textState.get().length(), textState
);
@Override
public Component render() {
return new Container(new ContainerProps().paddingAll(20)).children(
new Input(textState),
new Text(textLenghtComputed)
);
}
}
Como funciona, na prática
textStateé umState<String>comum, exatamente como oState<Integer>do contador — só que guardando texto.textLenghtComputedé criado comComputedState.of(supplier, dependências...). O primeiro argumento é a lógica de cálculo (uma lambda), e o segundo é a lista de estados que, ao mudar, disparam o recálculo automático.- O
Input(textState)já vem conectado diretamente ao estado — cada tecla digitada atualizatextStateimediatamente. - O
Text(textLenghtComputed)simplesmente exibe o valor computado, sem saber (nem precisar saber) de onde ele vem.
O resultado: digitar no campo atualiza o texto de tamanho instantaneamente, sem uma única linha de código de "atualização manual".
Por que isso importa
Esse padrão de ComputedState é a base para qualquer coisa mais complexa: validação de formulário em tempo real, contadores de caracteres restantes, campos calculados (tipo total de um carrinho de compras), filtros de lista baseados em texto digitado — tudo usa essa mesma ideia de "declarar a relação, deixar o framework propagar".
Publiquei também a versão desse post no Reddit, com mais contexto sobre o ecossistema Megalodonte:
Link do post no Reddit: https://www.reddit.com/r/JavaFX/s/6IU0PLkS71