Introducción

Redux es una librería de JavaScript para la gestión del estado de aplicaciones. Es especialmente útil en aplicaciones grandes donde el manejo del estado puede volverse complejo y difícil de mantener. Redux se basa en tres principios fundamentales:

  1. Estado Único: Toda la información de la aplicación se almacena en un único objeto de estado.
  2. Estado de Solo Lectura: El estado es inmutable y solo puede ser modificado emitiendo acciones.
  3. Cambios mediante Funciones Puras: Los cambios en el estado se realizan mediante funciones puras llamadas reductores (reducers).

Instalación de Redux

Para comenzar a usar Redux en tu proyecto, primero necesitas instalarlo. Puedes hacerlo usando npm o yarn:

npm install redux react-redux

O con yarn:

yarn add redux react-redux

Conceptos Básicos de Redux

Estado (State)

El estado en Redux es un objeto que contiene toda la información de la aplicación. Este estado es inmutable y solo puede ser modificado mediante acciones.

Acciones (Actions)

Las acciones son objetos que describen un cambio en el estado. Cada acción debe tener una propiedad type que indica el tipo de acción a realizar. Opcionalmente, pueden incluir datos adicionales.

const incrementAction = {
  type: 'INCREMENT',
  payload: 1
};

Reductores (Reducers)

Los reductores son funciones puras que toman el estado actual y una acción como argumentos, y devuelven un nuevo estado. No deben tener efectos secundarios ni modificar el estado directamente.

const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + action.payload };
    case 'DECREMENT':
      return { count: state.count - action.payload };
    default:
      return state;
  }
}

Store

El store es el objeto que contiene el estado de la aplicación. Se crea usando el reductor y proporciona métodos para acceder al estado, despachar acciones y suscribirse a cambios en el estado.

import { createStore } from 'redux';

const store = createStore(counterReducer);

Ejemplo Práctico

Vamos a crear una pequeña aplicación de contador usando Redux.

Paso 1: Definir Acciones

// actions.js
export const increment = (value) => ({
  type: 'INCREMENT',
  payload: value
});

export const decrement = (value) => ({
  type: 'DECREMENT',
  payload: value
});

Paso 2: Crear el Reductor

// reducer.js
const initialState = { count: 0 };

export function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + action.payload };
    case 'DECREMENT':
      return { count: state.count - action.payload };
    default:
      return state;
  }
}

Paso 3: Crear el Store

// store.js
import { createStore } from 'redux';
import { counterReducer } from './reducer';

export const store = createStore(counterReducer);

Paso 4: Conectar React con Redux

Para conectar React con Redux, usamos el paquete react-redux.

// App.js
import React from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import { store } from './store';
import { increment, decrement } from './actions';

function Counter() {
  const count = useSelector((state) => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => dispatch(increment(1))}>Increment</button>
      <button onClick={() => dispatch(decrement(1))}>Decrement</button>
    </div>
  );
}

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

export default App;

Ejercicio Práctico

Ejercicio

Crea una aplicación de lista de tareas (To-Do List) usando Redux. La aplicación debe permitir agregar, eliminar y marcar tareas como completadas.

Requisitos

  1. Acciones: Define acciones para agregar, eliminar y marcar tareas.
  2. Reductor: Crea un reductor que maneje las acciones definidas.
  3. Store: Configura el store con el reductor.
  4. Componentes: Crea componentes React para mostrar y manipular la lista de tareas.
  5. Conexión: Conecta los componentes React con Redux usando react-redux.

Solución

Acciones

// actions.js
export const addTask = (task) => ({
  type: 'ADD_TASK',
  payload: task
});

export const removeTask = (id) => ({
  type: 'REMOVE_TASK',
  payload: id
});

export const toggleTask = (id) => ({
  type: 'TOGGLE_TASK',
  payload: id
});

Reductor

// reducer.js
const initialState = { tasks: [] };

export function tasksReducer(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TASK':
      return { tasks: [...state.tasks, { id: Date.now(), text: action.payload, completed: false }] };
    case 'REMOVE_TASK':
      return { tasks: state.tasks.filter(task => task.id !== action.payload) };
    case 'TOGGLE_TASK':
      return {
        tasks: state.tasks.map(task =>
          task.id === action.payload ? { ...task, completed: !task.completed } : task
        )
      };
    default:
      return state;
  }
}

Store

// store.js
import { createStore } from 'redux';
import { tasksReducer } from './reducer';

export const store = createStore(tasksReducer);

Componentes

// App.js
import React, { useState } from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import { store } from './store';
import { addTask, removeTask, toggleTask } from './actions';

function TaskList() {
  const tasks = useSelector((state) => state.tasks);
  const dispatch = useDispatch();
  const [task, setTask] = useState('');

  const handleAddTask = () => {
    if (task.trim()) {
      dispatch(addTask(task));
      setTask('');
    }
  };

  return (
    <div>
      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="Add a new task"
      />
      <button onClick={handleAddTask}>Add Task</button>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>
            <span
              style={{ textDecoration: task.completed ? 'line-through' : 'none' }}
              onClick={() => dispatch(toggleTask(task.id))}
            >
              {task.text}
            </span>
            <button onClick={() => dispatch(removeTask(task.id))}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

function App() {
  return (
    <Provider store={store}>
      <TaskList />
    </Provider>
  );
}

export default App;

Conclusión

En esta sección, hemos aprendido los conceptos básicos de Redux y cómo integrarlo con React para gestionar el estado de una aplicación. Hemos cubierto la creación de acciones, reductores y el store, así como la conexión de componentes React con Redux. Además, hemos implementado una aplicación de contador y una lista de tareas para practicar estos conceptos.

En el siguiente módulo, exploraremos más sobre los frameworks y librerías de JavaScript, comenzando con una introducción a React.

JavaScript: De Principiante a Avanzado

Módulo 1: Introducción a JavaScript

Módulo 2: Estructuras de Control

Módulo 3: Funciones

Módulo 4: Objetos y Arrays

Módulo 5: Objetos y Funciones Avanzadas

Módulo 6: El Modelo de Objetos del Documento (DOM)

Módulo 7: APIs del Navegador y Temas Avanzados

Módulo 8: Pruebas y Depuración

Módulo 9: Rendimiento y Optimización

Módulo 10: Frameworks y Librerías de JavaScript

Módulo 11: Proyecto Final

© Copyright 2024. Todos los derechos reservados