style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Redux-mx

React es una herramienta que ha cambiado el desarrollo web y nos ayuda a recordar lo difícil que es controlar el estado de una aplicación, sobre todo si manejas hilos, llamadas asíncronas y muchos clientes simultáneos, a pesar de que una aplicación web no maneja hilos normalmente, tiene muchas interacciones entre el servidor, la acciones del usuario y en muchos casos timers que monitorean alguna actividad.

Debido a esta complejidad es que nació como arquitectura Flux y después Redux como librería que implementa la arquitectura Flux, Redux es tan bueno que casi casi se volvio la utileria por defecto para control de estado con React, su creador actualmente trabaja para Facebook mejorando React. Redux ha permitido que las aplicaciones sean predecibles y mas sencillas de desarrollar. No todas las aplicaciones lo necesitan pero a mi en lo personal me ha tocado el caso en el que lo necesitaba y no lo tenia (todavía no existía).

Ahora que lo uso bastante en conjunto con React puedo decir que me parecen muy agradables de usar en el día a día, ahora contruyo apps con menos errores, mas complejas y en tiempos mas cortos (comparando con angular y jQuery). Pero esto es un foro de java y no de javascript. Pues bien ahi esta la cosa, tanto me gusta Redux que creo seria conveniente, con esta onda de los microservicios, tener Redux como control del estado de mi servidor y usarlo de una forma muy similar a como actualmente lo uso en mi aplicación web.

Fabricando librerias

La intención no es migrar todas las funcionalidad que Redux ofrece, pero al menos si las siguientes

- Suscribirse a los cambios de estado
- Creacion del store
- Acciones
- Reducers
- Middlewares

Hasta el momento solo tengo los primeros 4 puntos, estoy trabajando en los midddlewares. El proyecto esta en Redux-mx aun no lo publico en Maven Central, por lo que si lo quieren usar se debe instalar manualmente.

Solo tiene como dependencia Vavr, librería que ya mencione con anterioridad. Nota importante uso RuntimeExceptions para configuraciones inadecuadas de los reducers y las acciones (Mismas reglas que Redux).

Action

En Redux las acciones contienen la información que sera almacenada en el estado, pero no afectan el estado directamente. Toda acción debe tener un tipo y un payload, el tipo para ser flexibles lo deje en String y el payload es un HashMap immutable de vavr.

Crear un accion en Redux-mx:

        new Action(ADD_TODO2, HashMap.of("text2", “hello”));

En Redux js

        {type:ADD_TODO2, text2: ‘hola’}

Reducer

Los reducers escuchan las acciones y actualizan el estado, de igual forma se requiere que el estado sea immutable por lo que el estado es un hashmap de vavr

Crear un Reducer en Redux-mx:

public static Reducer reducer = (state, action) -> {
    switch (action.type) {
        case ADD_TODO2: {
            return state.put("text2", action.payload.get("text2").getOrNull());
        }
        default: {
            return state;
        }
    }
};

En Redux js con Ramda js

export default (state, action) => {
        switch(action.type) {
                case ADD_TODO2: {
                        return R.merge(state, { text: action.text });
                }
                default: {
                        return state;
                }
        }
}

Store

El store es el “guardian” del estado, aqui obtenemos las funciones subscribe, dispatch, getState y combineReducers. La filosofía de Redux es tener un solo estado y no un estado por cada modulo, controlar pequeñas porciones del estado con reducers pero combinar todos los reducers en uno solo.

Crear un Store en Redux-mx con 2 reducers:

Reducer appReducer = Store.combineReducer(HashMap.of(
        “modulo1”, Modulo1.reducer,
        “Modulo2”, Modulo2.reducer));

Store store = new Store(appReducer, initialState);

Crear el Store en Redux js

Reducer appReducer = combineReducers({
  modulo1,
  modulo2
});

Const store = createStore(appReducer, initialState);

Combinando todo

La aplicación consta de 2 modulos uno que tiene solo texto y otro que tiene texto2, tenemos 2 acciones una para actualizar el texto y otra para el texto 2, la estructura de directorios de patos nos dice que las acciones, los tipos de accion y los reducers deben estar en el mismo archivo.

Modulo1

package examples.simple.modules;

import io.vavr.collection.HashMap;
import mx.com.betotto.redux.Action;
import mx.com.betotto.redux.Reducer;

public class Modulo1 {

    private final static String ADD_TODO = "ADD_TODO";

    public static Reducer reducer = (state, action) -> {
        switch (action.type) {
            case ADD_TODO: {
                return state.put("text", action.payload.get("text").getOrNull());
            }
            default: {
                return state;
            }
        }
    };

    public static Action addTodo(String todoText) {
        return new Action(ADD_TODO, HashMap.of("text", todoText));
    }
}

Modulo2

package examples.simple.modules;

import io.vavr.collection.HashMap;
import mx.com.betotto.redux.Action;
import mx.com.betotto.redux.Reducer;

public class Modulo2 {

    private final static String ADD_TODO2 = "ADD_TODO2";

    public static Reducer reducer = (state, action) -> {
        switch (action.type) {
            case ADD_TODO2: {
                return state.put("text2", action.payload.get("text2").getOrNull());
            }
            default: {
                return state;
            }
        }
    };

    public static Action addTodo2(String todoText) {
        return new Action(ADD_TODO2, HashMap.of("text2", todoText));
    }
}

Usando los modulos

Pues bien ya todo conectado simplemente vamos a crear el estado inicial y a partir de ahi vamos a lanzar las diferentes acciones.

package examples.simple;

import examples.simple.modules.Modulo1;
import examples.simple.modules.Modulo2;
import io.vavr.collection.HashMap;
import mx.com.betotto.redux.Reducer;
import mx.com.betotto.redux.Store;

public class Main {

    private static HashMap<String, Object> initialState = HashMap.of("modulo1", HashMap.of("text", ""),"modulo2", HashMap.of("text2", ""));

    public static void main(String... args) {

        Reducer appReducer = Store.combineReducer(HashMap.of(
                "modulo1", Modulo1.reducer,
                "modulo2", Modulo2.reducer));

        Store store = new Store(appReducer, initialState);

        Runnable unsubcribe = store.subscribe(s -> System.out.println(s));

        store.dispatch(Modulo1.addTodo("Hello"));

        store.dispatch(Modulo2.addTodo2("Hello"));

        unsubcribe.run();

        store.dispatch(Modulo2.addTodo2("Hello3"));
    }
}

Ok el estado inicial como puedes ver es un HashMap de Vavr, en javascript seria un simple objeto con las llaves modulo1 y modulo2 ambos objetos con llave text y text2 respectivamente. Inicializamos el estado, y creamos un listener de los cambios del estado, que de momento solo los manda a consola. La función/metodo subscribe nos regresa una función para desuscribirnos, no quiere decir que no habra mas cambios de estado, esos están ahi, pero ya no los escucharemos. La salida de este código es:

HashMap((modulo1, HashMap((text, Hello))), (modulo2, HashMap((text2, ))))
HashMap((modulo1, HashMap((text, Hello))), (modulo2, HashMap((text2, Hello))))

Si comentamos la linea unsubcribe.run()

HashMap((modulo1, HashMap((text, Hello))), (modulo2, HashMap((text2, ))))
HashMap((modulo1, HashMap((text, Hello))), (modulo2, HashMap((text2, Hello))))
HashMap((modulo1, HashMap((text, Hello))), (modulo2, HashMap((text2, Hello3))))

Siguientes pasos

Este proyecto aun no esta listo para producción, falta agregar los middlewares, pruebas unitarias, documentación y algunas optimizaciones en las actualizaciones de estado, pero sirve como punto de partida. Si el código del ejemplo no esta muy claro, en el repositorio puedes ver el paquete examples/simple, ahi están el main y los dos modulo de este ejemplo.

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">