Gestión de Estado con Redux: Fundamentos y Ejemplos Prácticos

Redux simplifica la gestión del estado en aplicaciones JavaScript, asegurando la consistencia del comportamiento y facilitando las pruebas y el rastreo.

El flujo básico de Redux comprende los siguientes pasos:

  • createStore: Crea el contenedor principal del estado (store).
  • reducer: Una función pura que define cómo evoluciona el estado. Recibe el estado anterior y una acción, y devuevle el nuevo estado.
  • getState: Permite consultar el estado actual del store.
  • dispatch: Envía acciones al store para iniciar actualizaciones de estado.
  • subscribe: Permite registrar observadores que se notificarán ante cualquier cambio en el estado.

Ejemplo de Sincronización Básica con Redux

El siguiente código ilustra una implementación simple de Redux para un contador.


// App.js
import React from "react";
import CounterView from "./views/CounterView";

function App() {
  return (
    <div className="App">
      <CounterView />
    </div>
  );
}

export default App;

// store/counterStore.js
import { createStore } from "redux";

// Reducer para manejar las operaciones del contador
function counterReducer(currentState = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return currentState + 1;
    case "DECREMENT":
      return currentState - 1;
    default:
      return currentState;
  }
}

const appStore = createStore(counterReducer);
export default appStore;

// views/CounterView.js
import React, { Component } from "react";
import appStore from "../store/counterStore";

export default class CounterView extends Component {
  componentDidMount() {
    // Suscribirse a los cambios del store
    this.unsubscribe = appStore.subscribe(() => {
      // Forzar la actualización del componente al cambiar el estado
      this.forceUpdate();
    });
  }

  handleIncrement = () => {
    // Despachar la acción para incrementar
    appStore.dispatch({ type: "INCREMENT" });
  };

  handleDecrement = () => {
    // Despachar la acción para decrementar
    appStore.dispatch({ type: "DECREMENT" });
  };

  componentWillUnmount() {
    // Limpiar la suscripción al desmontar el componente
    if (this.unsubscribe) {
      this.unsubscribe();
    }
  }

  render() {
    // Obtener el estado actual
    const count = appStore.getState();
    return (
      <div>
        <h3>Contador Redux</h3>
        <p>Valor: {count}</p>
        <button onClick={this.handleIncrement}>Aumentar</button>
        <button onClick={this.handleDecrement}>Disminuir</button>
      </div>
    );
  }
}

Manejo de Operaciones Asíncronas con Middleware (Redux-Thunk)

Por defecto, dispatch solo acepta objetos de acción. Para manejar lógica asíncrona, como llamadas a APIs, se utilizna middlewares. redux-thunk es una opción popular.


// store/asyncCounterStore.js
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import logger from "redux-logger"; // Middleware para logging de acciones

// Reducer actualizado para aceptar payload
function asyncCounterReducer(currentState = 0, action) {
  switch (action.type) {
    case "ADD_VALUE":
      return currentState + (action.payload || 1);
    case "SUBTRACT_VALUE":
      return currentState - (action.payload || 1);
    default:
      return currentState;
  }
}

// Aplicar middlewares al crear el store
const asyncAppStore = createStore(asyncCounterReducer, applyMiddleware(thunk, logger));
export default asyncAppStore;

// views/AsyncCounterView.js
import React, { Component } from "react";
import asyncAppStore from "../store/asyncCounterStore";

export default class AsyncCounterView extends Component {
  componentDidMount() {
    this.unsubscribe = asyncAppStore.subscribe(() => {
      this.forceUpdate();
    });
  }

  increment = () => {
    asyncAppStore.dispatch({ type: "ADD_VALUE", payload: 5 });
  };

  decrement = () => {
    asyncAppStore.dispatch({ type: "SUBTRACT_VALUE", payload: 5 });
  };

  // Acción asíncrona usando thunk
  asyncIncrement = () => {
    asyncAppStore.dispatch((dispatch, getState) => {
      console.log("Iniciando operación asíncrona...");
      // Simulación de llamada a API con setTimeout
      setTimeout(() => {
        console.log("Operación asíncrona completada.");
        // Despachar la acción de actualización después del retraso
        dispatch({ type: "ADD_VALUE", payload: 10 });
      }, 1500);
    });
  };

  componentWillUnmount() {
    if (this.unsubscribe) {
      this.unsubscribe();
    }
  }

  render() {
    const currentCount = asyncAppStore.getState();
    return (
      <div>
        <h3>Contador Asíncrono con Redux</h3>
        <p>Valor: {currentCount}</p>
        <button onClick={this.increment}>Aumentar (+5)</button>
        <button onClick={this.decrement}>Disminuir (-5)</button>
        <button onClick={this.asyncIncrement}>Aumento Asíncrono (+10)</button>
      </div>
    );
  }
}

Organización del Estado con combineReducers

Para aplicaciones más grandes, es recomendable dividir el estado en partes más pequeñas y manejarlas con combineReducers.


// store/combinedStore.js
import { createStore, applyMiddleware, combineReducers } from "redux";
import thunk from "redux-thunk";
import logger from "redux-logger";

// Reducer para el contador
export const counterReducer = (state = 0, { type, payload = 1 }) => {
  switch (type) {
    case "ADD":
      return state + payload;
    case "SUBTRACT":
      return state - payload;
    default:
      return state;
  }
};

// Se pueden definir otros reducers aquí para diferentes partes del estado

// Combinar todos los reducers en uno solo
const rootReducer = combineReducers({
  counterState: counterReducer,
  // otherState: otherReducer,
});

// Crear el store con el rootReducer y middlewares
const configuredStore = createStore(
  rootReducer,
  applyMiddleware(thunk, logger)
);

export default configuredStore;

Para acceder al estado combinado:


// Acceso al estado específico del contador
<p>Valor del contador: {configuredStore.getState().counterState}</p>

Etiquetas: Redux JavaScript gestión de estado React redux-thunk

Publicado el 7-10 17:44