Ir al contenido principal

Redux, estado de la aplicación 

Redux, es un framework para desarrollo web, que nos provee de herramientas para controlar el estado de nuestra aplicación en un estandarizado patrón de arquitectura hecho por Flux. Se puede ver todo su potencial en una SPA (Single Page Application), es decir una aplicación web de una sola página. Es mucho más ligero que el patrón Flux, y además te permite mantener todos tus estados juntos, en uno solo.

Redux nace de la necesidad de controlar el estado de nuestra aplicación y además poder dar vuelta atrás a alguna acción de usuario, o por algún error en respuesta del servidor. Controlar una aplicación robusta, en donde tengamos que actualizar vistas y controladores , se vuelve difícil cuando cada uno tiene una estado propio, porque además de repetir muchas veces el código, en ocasiones no guardamos el estado antes de alguna acción o evento del usuario. Entonces nuestra aplicación se vuelve menos intuitiva porque tenemos que darle al usuario de nuevo un formulario en blanco, o mostrarle bienvenida a la interfaz de usuario. 
Redux almacena todos esos estados, con una función especial (combineReducers), que te permite crear estados individuales para diversas vistas, y agregarlos a un árbol de estado global, al que se puede acceder fácilmente. 

Empecemos explicando la ideología y filosofía de Redux, en este patrón existen 3 partes esenciales: Reductores o funciones de estados (Reducers), Acciones (Actions), Funciones modificadoras de estados (Dispatcher). 

Hablemos de los 3 aspectos:
  • Actions
En esencia son funciones también, pero estás deben retornar un objeto JSON con una propiedad obligatoria tipo (type) cada vez que son llamada, son tomadas por los reducers para realizar las actualizaciones de estados.


  • Reducers


También son llamados funciones de estado, controlan todo el estado de la vista, inicialmente deben retornar el estado inicial de aplicación y por cada acción recibida, retorna un nuevo estado, NUNCA modificar el estado actual.
  • Dispacher
Para hacer modificaciones de estado, Redux nos provee de un "despachador", que es el encargado de hacer las modificaciones de estado, recibe un modificador de estado, retornado por nuestra función de acción, y la parte del estado que se desea modificar.

Ahora, hagamos un ejemplo de cómo utilizar correctamente Redux. Con una aplicación que crea una tabla, te permite, elimina, filtrar e incluso modificar los datos. Redux, puede usar con o sin un framework o librería, (jQuery, Material), en este ejemplo lo usaremos sin ninguno de ellos sólo utilizaremos los estilos CSS de Bootstrap.
Configuramos nuestro entorno en un archivo html así:


<!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="UTF-8">
 <title>Redux Example</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"/>
 <link rel="stylesheet" href="index.css"/>
</head>
<body>
<!--Librería para usar, funciones de Array (map, filter, find, concat) y Object (assign)-->
 <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js"></script>
<!--Redux-->


 <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"></script>
</body>
</html>

Primero crearemos funciones de acción en un archivo index.js:


var Redux       = window.Redux,

    createStore = Redux.createStore;

var ADD = 'ADD', UPDATE = 'UPDATE', DELETE = 'DELETE', FILTER = 'FILTER';

//Actions REDUX
/*
* ADD PERSONA
* Agregar una persona
*/
function addPersona(persona){
return {
type: ADD,
persona: persona
}
}

/*

* FILTER TABLE
* Filtrar la tabla
*/
function filterTableByArea(area){
return {
type: FILTER,
area: area
}
}

/*

* UPDATE PERSONA
* Actualizar una persona
*/
function updatePersona(persona){
return {
type: UPDATE,
persona: persona
}
}
/*
* DELETE PERSONA
* Eliminar una persona
*/
function deletePersona(personaId){
return {
type: DELETE,
id: personaId
}

}


Estás funciones nos ayudarán a la hora de modificar el estado de nuestra aplicación.
Ahora crearemos nuestro Reducer para la tabla que controlará el estado global:


/*
* Estado global de la aplicación
*/
function todoList(state, action){
      //Estado inicial de la aplicación
state = state || {
todos: []
}

switch(action.type){
               //Caso para agregar una persona
case ADD:
if(!action.persona.id){
action.persona.id = state.todos.length + 1
}
return Object.assign({}, state, {
todos: state.todos.concat(action.persona)
})
               //Filtrar la tabla
case FILTER: 
applyFilterTable([].concat(state.todos), action.area)
return state
                //Actualizar un dato de la tabla
case UPDATE:
return Object.assign({}, state, {
todos: updateTablePersona(state.todos, action.persona)
})
                //Eliminar
case DELETE: 
return Object.assign({}, state, {
todos: deleteTablePersona(state.todos, action.id)
})
default
return state
}
return state

}

Ahora crearemos funciones auxiliares para modificar los estados


//Aplicar un filtro

function applyFilterTable(todos, area){
var tableBody = document.getElementById('todolist')

while(tableBody.firstChild) {
  tableBody.removeChild(tableBody.firstChild)
 }

if(area === 'todo'){
todos.forEach(function(persona){
return addTablePersona(persona)
})
}else{
todos.filter(function(persona){
return persona.area === area
}).forEach(function(persona){
return addTablePersona(persona)
})
}
}

//Actualizar tabla

function updateTablePersona(todos, personaUpdate){
return todos.map(function(persona){
if(persona.id === personaUpdate.id){

var newPersona = {}

if(personaUpdate.name){
newPersona.name = personaUpdate.name
var cellName = persona.tableRow.firstChild
while(cellName.firstChild){
cellName.removeChild(cellName.firstChild)
}

cellName.appendChild( document.createTextNode(personaUpdate.name) )
}

if(personaUpdate.area){
newPersona.area = personaUpdate.area
var cellArea = persona.tableRow.childNodes[1]
while(cellArea.firstChild){
cellArea.removeChild(cellArea.firstChild)
}

cellArea.appendChild( document.createTextNode(personaUpdate.area) )
}

return Object.assign({}, persona, newPersona)
}
return persona
})

}

Después algo de vista para aplicación, una tabla, un formulario para agregar, y unas herramientas para filtrar los datos.

Así se vería nuestro código html:


 <div id="todo" class="table-responsive">
  <div class="input-group">
   <label for="filter" class="input-group-addon">Filtrar</label>
  <select name="filter" id="filter-todo" class="form-control" onchange="filterTodo(event)">
   <option value="todo" selected>Todos</option>
   <option value="contabilidad">Contabilidad</option>
   <option value="sistemas">Sistemas</option>
  </select>
  </div>
  <table class="table">
   <thead>
    <tr>
     <th>Nombre</th>
     <th>Area</th>
     <th>Herramientas</th>
    </tr>
   </thead>
   <tbody id="todolist"></tbody>
  </table>
 </div>
 <form action="/add" onsubmit="addTodo(event)" id="form-add-persona">
  <legend>Agregar</legend>
  <fieldset class="form-group">
   <label for="nombre">Nombre</label>
   <input type="text" placeholder="Nombre" name="nombre" id="inputName" class="form-control" required/>
  </fieldset>
  <fieldset class="form-group">
   <label for="area">Area</label>
   <select name="area" id="selectArea" class="form-control">
    <option value="contabilidad">Contabilidad</option>
    <option value="sistemas" selected>Sistemas</option>
   </select>
  </fieldset>
  <button type="submit" class="btn btn-default" id="btn-add">Agregar</button>

 </form>


Y así se vería nuestra aplicación real.



Antes de hacer algo con nuestra aplicación, debes crear el estado de la aplicación, eso debe hacer con la función createStore de Redux, es importante que creemos el estado antes de hacer algo con él, y asignarlo a una variable global. Para que todas las funciones activadoras, puedan acceder al estado, el objeto obtiene varios métodos para acceder al estado, entre ellos:

  • getState
Obtiene el estado de la aplicación.
  • susbcribe
Agrega una función que se activa cada vez que el estado cambia
  • dispatch
Lanza una acción al estado para modificarlo

Ahora agreguemos los activadores de acciones, es decir desde dónde se puede cambiar el estado de nuestra aplicación:
Que es, cuando agregamos una nueva persona, eliminarla o editarla.
function deleteTablePersona(todos, idPersona){
var prevTodos = todos,
    buttonBack = document.getElementById('button-back');
    
return todos.filter(function(persona){
if(persona.id === idPersona){
persona.tableRow.parentNode.removeChild(persona.tableRow)
}
return persona.id !== idPersona
})
}

function filterTodo(e){
var select = e.target || this
var area = select.value

store.dispatch( filterTableByArea(area) )
}

function addTodo(e){
if(e && ( e.preventDefault || e.stopPropagation ) ){
e.preventDefault() && e.stopPropagation()
}

var inputName = document.getElementById('inputName'),
    selectArea = document.getElementById('selectArea'),
    name = inputName.value,
    area = selectArea.value,
    persona = {
    name: name,
    area: area
    };

var storePersona = addTablePersona(persona)
store.dispatch(addPersona(storePersona))

inputName.value = ''

}

/*

* Manejadores de los eventos para los botones de las personas
*/

/*

* UPDATE PERSONA
*/
function handleUpdatePersona(persona){
var formUpdate = document.getElementById('form-add-persona'),
    inputName = document.getElementById('inputName'),
    selectArea = document.getElementById('selectArea'),
    buttonCalcel = document.createElement('button'),
    btnAdd = document.getElementById('btn-add');

    function changeUpdateToAdd(){
    formUpdate.onsubmit = addTodo
    formUpdate.removeChild(buttonCalcel)
    while(btnAdd.firstChild){
    btnAdd.removeChild(btnAdd.firstChild)
    }

    btnAdd.appendChilddocument.createTextNode('Agregar') )
    inputName.value = ''
    }

    buttonCalcel.setAttribute('class', 'btn btn-default')
    buttonCalcel.appendChilddocument.createTextNode('Cancelar') )

    buttonCalcel.onclick = changeUpdateToAdd


    formUpdate.appendChild(buttonCalcel)
    while(btnAdd.firstChild){
    btnAdd.removeChild(btnAdd.firstChild)
    }

    btnAdd.appendChilddocument.createTextNode('Modificar') )

    inputName.value = persona.name

    selectArea.value = persona.area


formUpdate.onsubmit = function(e){
e.preventDefault()
var newPersona = Object.assign({}, persona, {name: inputName.value, area: selectArea.value})
store.dispatch(updatePersona(newPersona))

changeUpdateToAdd()
}

}


/*

* DELETE PERSONA
*/
function handleDeletePersona(idPersona){
return store.dispatch(deletePersona(idPersona))
}

//Agregar una fila a tabla con la nueva persona

function addTablePersona(persona){
var table = document.getElementById('todolist'),
    tableRow = document.createElement('tr'),
    tableCellName = document.createElement('td'),
    tableCellArea = document.createElement('td'),
    tableCellTools = document.createElement('td'),
    buttonUpdate = document.createElement('button'),
    buttonDelete = document.createElement('button');

   buttonDelete.appendChilddocument.createTextNode('Eliminar') )
   buttonUpdate.appendChilddocument.createTextNode('Actualizar') )

   buttonDelete.setAttribute('class', 'btn btn-default')
   buttonUpdate.setAttribute('class', 'btn btn-default')

   buttonDelete.onclick = function(){
    handleDeletePersona(persona.id)
   }

   buttonUpdate.onclick  = function(){
    handleUpdatePersona(persona)
   }

  tableCellArea.appendChilddocument.createTextNode(persona.area) )
  tableCellName.appendChilddocument.createTextNode(persona.name) )
  tableCellTools.appendChild( buttonUpdate )
  tableCellTools.appendChild( buttonDelete )
tableRow.appendChild(tableCellName)
tableRow.appendChild(tableCellArea)
tableRow.appendChild(tableCellTools)
table.appendChild(tableRow)
persona.tableRow = tableRow
 return persona
}

Es una verdadera aplicación, que crea una tabla con datos, todo lo que un día quisimos, y con Redux, podemos controlar su estado en un sólo sentido.
Si quieres probarlo, puedes encontrar los archivos en el siguiente enlace.
O en la siguiente dirección https://github.com/Soluciones-SORE/example-redux

Comentarios

Entradas populares de este blog

Reset de almohadillas  para impresora Canon PIXMA MG3510 Para cuando se produce los siguientes errores 5B02, 5B03, 5B04, 5B05, 5B12, 5B13, 5B14, 5B15. Esto se puede deber a que la impresora haya alcanzado su  límite de impresiones; sucede por varias razones (las hojas a color o impresiones fotográficas: estas pueden contar el triple, según su tamaño en el contador de impresiones).  Y aún más cuando a este modelo de impresoras se le adapto un sistema de tinta continua la vida de las almohadillas suele ser más corto cunado dicho sistema tienen goteos de tinta sin que el usuario se dé cuenta de ello .  Pasos a seguir: 1.- pasos solo en la impresora si el programa aun abierto 1.          Apagar la impresora para entrar en  modo mantenimiento o servicio 2.        Mantenemos pulsado el botón  Stop / Reset 3.        Pulsamos e...

Resetear contador almohadillas en Epson L210

Resetear contador almohadillas en Epson L210 ¿Que son las almohadillas de la impresora Epson? las Almohadillas de las impresoras Epson Stylus son bloques de diferentes tamaños (dependiendo del modelo de la impresora) que están fabricadas de un material fibroso y absorbente. Todas las impresoras Epson Stylus las tienen, estas están ubicadas en un contenedor dentro de la impresora, algunos las tienen en la parte baja y otros en la parte posterior de la impresora en el cual se va acumulando la tinta que el cabezal va botando cada vez que se enciende la impresora o cuando realiza una limpieza de sus inyectores o cabezal y cuando va realizando una impresión. Cuando se llenan estas almohadillas, la impresora tienen en su tarjeta principal un contador electrónico llamado memoria EEprom que contabiliza cuanta tinta es acumulada dentro de la impresora. Una vez que la memoria EEProm llega a su cuenta límite establecido simplemente bloquea la impresora evitando que la tinta de des...

Resetear contador almohadillas en Epson L3150

Resetear contador almohadillas en EpsonL3150 ¿Que son las almohadillas de la impresora Epson? las Almohadillas de las impresoras Epson  son bloques de diferentes tamaños (dependiendo del modelo de la impresora) que están fabricadas de un material fibroso y absorbente. Todas las impresoras Epson las tienen, estas están ubicadas en un contenedor dentro de la impresora, algunos las tienen en la parte baja y otros en la parte posterior de la impresora en el cual se va acumulando la tinta que el cabezal va botando cada vez que se enciende la impresora o cuando realiza una limpieza de sus inyectores o cabezal y cuando va realizando una impresión. Cuando se llenan estas almohadillas, la impresora tienen en su tarjeta principal un contador electrónico llamado memoria EEprom que contabiliza cuanta tinta es acumulada dentro de la impresora. Una vez que la memoria EEProm llega a su cuenta límite establecido simplemente bloquea la impresora evi...