Qué es el estado en frameworks declarativos

La tecnología evoluciona a una velocidad vertiginosa y esto provoca que constantemente tengamos que evolucionar nuestros conocimientos.

Por norma general estos nuevos frameworks traen mejoras con respecto a los frameworks o tecnologías ya existentes, pero a veces traen consigo una serie de problemas o retos a resolver.

Es tan importante evolucionar nuestros conocimientos especificos a tecnologías o frameworks que van surgiendo como entenderlas bien así como los problemas que traen consigo.

Este conocimiento junto con el de las distintas herramientas como buenas prácticas, patrones, librerías que tenemos a nuestro alcance nos va a proporcionar de la capacidad de poder tomar las mejores decisiónes en base a nuestro conocimiento y experiencia.

En este artículo vamos a hablar del estado en frameworks declarativos y porque es importante entender bien este concepto.

Frameworks imperativos

Antes de explicar en que consiste un framework declarativo es importante recordar lo qué es un framework imperativo.

Un framework imperativo es aquél donde las vistas se actualizan en base a instrucciones imperativas desde el código.

La forma tradicional de actualizar las vistas en Android o iOS es mediante intrucciones imperativas.

Veamos un ejemplo en Android utlizando Kotlin:

example_text_view.text = "Example"

val adapter = ExampleAdapter(items)
example_recyclerview.adapter = adapter

adapter.items = newItems

Frameworks declarativos

Un framework declarativo es aquel donde las vistas se generan y actualizan en base a unos datos con los que la vista esta enlazada, de forma que cuando estos datos cambian la vista se renderiza nuevamente en base a estos nuevos datos. Para ello utiliza un sistema de binding entre la vista y los datos.

Los datos con los que se enlaza una vista en un framework declarativo es lo que se conoce como estado.

Por lo tanto las vistas en frameworks declarativos se crean o se actualizan automáticamente en función del estado..

Ejemplos de frameworks declarativos

Frameworks como React.js o Vue.js llevan ya llevan unos años y nacieron desde el principio como declarativos y tenían como objetivo mejorar el desarrollo de aplicaciones web en JavaScript, basándose en el principio de Single Page Application (SPA).

Este año Google anunció Jetpack Compose y Apple anunció también SwiftUI, ambas son unas nuevas librerías que utilizan renderización declarativa para crear interfaces en Android y en iOS.

Flutter es un framework de desarrollo cros-platform creado por Google que utiliza Dart como lenguaje, inicialmente desarrollado para crear aplicaciones Android, iOS que se basa renderización declarativa.

Renderización declarativa en React.js

Como hemos visto la renderización declarativa consiste en la generación de la interfaz de usuario en base a un estado.

Veamos un ejemplo en React.js:

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

A simple vista puede parecer similar a renderizar una plantilla html, pero React ha hecho por debajo trabajo por nosotros.

La vista esta enlazada al estado count del componente y reacciona de forma reactiva renderizando el componente completamente cada vez que count cambia.

De este modo cada vez que pulsamos el botón en la interfaz lo que vemos es que se actualiza el contador dentro del elemento <p>.

Pero en realizad se esta renderizando el componente complementamente cada vez que se actualiza el estado con el que esta enlazado.

React-counter

Renderización declarativa en Flutter

Como también hemos visto, la renderización declarativa no es algo particar de JavaScript.

Este framework también se basa también en renderización declarativa.

Podemos realizar algo similar al ejemplo de React:

class Counter extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return _CounterState();
  }
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$_counter',
            style: Theme.of(context).textTheme.display1,
          ),
          FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ), // This trailing comma m
        ],
      ),
    );
  }
}

Y tenemos una renderización declarativa con un resultado similar:

flutter-counter

Estado de los componentes

El estado son los datos con los que esta vinculada la vista o componente.

El estado puede ser modificado por el usuario interactuando con la aplicación.

Los componentes, widget o simplemente vistas se dividen en 3:

  • Sin estado
  • Con estado local o efímero
  • Con estado compartido o estado global.

Sin estado

Los componentes sin estado no guardan ninguna información y por ello no necesitan de datos locales.

Eso no significa que no puedan personalizar su comportamiento, lo que se consigue con la ayuda de las propiedades de los componentes.

Con estado local

El estado local o efímero es aquel que no es necesario compartir entre diferentes componentes de la aplicación o widget, dependiente de la plataforma donde estes desarrollando.

No es necesario persistir este estado de ninguna forma ya sea en memoria o en una base de datos.

Ejemplos de estado local:

  • Posición del scroll en una tabla
  • En un tab el tab activo
  • Si un elemento de una vista en forma de arbol esta expandido o no

Con estado global

El estado de aplicación o global es aquel que si es necesario compartir entre diferentes componentes de la aplicación y que quieres mantener incluso entre sesiones de usuario.

Este estado es necesario persistirlo de alguna forma.

Ejemplos de estado de aplicación:

  • Preferencias de usuario
  • Sesión de usuario
  • Carrito de la compra en un ecommerce
  • Notificaciones
  • Si una noticia ya ha sido leida por el usuario

Un reto importante en la gestión del estado es cuando este está compartido entre varios componentes, ya que todos ellos deberán actualizarse cuando el estado cambie.

Un carrito de la compra es un buen ejemplo.

shopping-cart

Sin reglas fijas

Si algo es estado local o global puede depender del contexto de la aplicación.

Por ejemplo, podrías decidir que según el contexto tu aplicación el índice seleccionado en un tab no es estado local. Tú puedes necesitar cambiarlo desde fuera y mantenerlo entre sesiones. En este caso sería estado de aplicación.

También puede ocurrir que algo que comienza siendo estado local y posteriormente lo refactorizamos a estado de aplicación o viceversa.

Os dejo un diagrama que puede ayudar a verlo más claro:

Estado en frameworks declarativos

Porqué hay que gestionar el estado

Los frameworks declarativos basado en componentes están pensados construir las interfaces de usuario como construcciones de lego, creando pequeñas piezas que posteriormente encajándolas todas juntas crean una interfaz.

La parte positiva de esto es que es más sencillo que en otras tecnologías dividir la interfaz en pequeñas abstracciones al igual que ya hacemos con el código dividiéndolo en clases. De esta forma evitamos tener vistas muy grandes que son dificiles de leer.

Cuando el framework es imperativo y esta dividido en pequeñas partes, desde la propia pantalla se iria comunicando a cada pequeña parte como se debe actualizar.

Sin embargo en los frameowrks declarativos esta forma de diseñar interfaces, junto con la parte declarativa del framework introduce un nuevo problema a resolver y es la gestión de estado cuando este debe ser compartido entre varios componentes.

En los frameworks declarativos los componentes solo reciben el aviso de que se tienen que actualizar pero no como. Ellos mismos de forma autónoma saben como actualizarse en base a unos datos con los que estan vinculados, que se conoce como estado.

Artículos Relacionados

Conclusiones

En este artículo hemos visto en que consiste el estado en frameworks declarativos.

También hemos visto la diferencia entre framework declarativo e imperativo.

La gestión del estado es un reto importante en frameworks declarativos y existen diferentes formas de gestionarlo.

Pero es muy importante entender bien en que consiste el estado antes de entrar a aplicar diferentes soluciones, patrones o librerías de moda donde a lo mejor no tiene sentido aplicarse.

Conocer en que consiste el estado es un pilar fundamental para poder decidir posteriormente que solución elegir para gestionarlo y saber donde aplicarla.