Creación de un Todo List con React Redux y Reducers

Clase 20 de 31Curso de React Avanzado

Resumen

Crear una Emoji To‑Do List con Redux Toolkit y React Redux es más simple de lo que parece. Aquí verás cómo tipar dispatch, leer el estado global con selector, mapear texto a emojis y despachar acciones para agregar y eliminar tareas, todo con TypeScript y un functional component.

¿Cómo se configura Redux Toolkit y React Redux en el proyecto?

Para usar el estado global necesitas instalar y configurar React Redux. Luego, importa el Provider y envuelve el componente principal con el store para que cualquier componente pueda acceder al estado.

¿Qué instalar y cómo importar React Redux?

  • Instala la librería: npm install react-redux.
  • Importa useDispatch y useSelector desde react-redux.
  • Usa Provider para inyectar el store.
// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import TodoList from './components/TodoList';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <TodoList />
    </Provider>
  </React.StrictMode>
);

¿Cómo tipar AppDispatch y RootState en TypeScript?

  • Importa los tipos creados en el store: AppDispatch y RootState.
  • Tipa dispatch con AppDispatch para despachar acciones con seguridad.
  • Tipa useSelector con RootState para acceder al estado con autocompletado.
import type { AppDispatch, RootState } from '../app/store';

¿Cómo se construye el componente TodoList con TypeScript?

El componente parte de un functional component con estado local para el texto del input, funciones para agregar y eliminar, y un mapeo de texto a emojis. La tecla Enter dispara la acción de agregar.

¿Cómo gestionar el estado local con useState?

  • Crea un estado local para el texto: todoText.
  • Limpia el estado tras agregar para escribir un nuevo todo.

¿Cómo mapear texto a emojis con toLowerCase?

  • Usa un objeto como emojiMap para asociar palabras a emojis.
  • Normaliza con toLowerCase y define un fallback: si no hay mapeo, usa el texto original.

¿Cómo capturar Enter y despachar addTodo?

  • Escucha onKeyDown en el input.
  • Si la tecla es Enter, ejecuta handleAddTodo.
// TodoList.tsx
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import type { AppDispatch, RootState } from '../app/store';
import { addTodo, removeTodo } from '../features/todos/todoActions';

type EmojiMap = { [key: string]: string };

const emojiMap: EmojiMap = {
  eat: '🍔',
  sleep: '🛏️',
  exercise: '🏋️',
};

const TodoList: React.FC = () => {
  const dispatch = useDispatch<AppDispatch>();
  const [todoText, setTodoText] = useState('');

  const handleAddTodo = () => {
    const mappedText = emojiMap[todoText.toLowerCase()] || todoText;
    const trimmed = mappedText.trim();
    if (!trimmed) return;
    dispatch(addTodo(trimmed));
    setTodoText('');
  };

  const handleRemoveTodo = (id: number) => {
    dispatch(removeTodo(id));
  };

  const todos = useSelector((state: RootState) => state.todos);

  return (
    <div>
      <p>Made with Redux Toolkit</p>
      <h1>Emoji To Do List</h1>

      <input
        type="text"
        value={todoText}
        onChange={(e) => setTodoText(e.target.value)}
        onKeyDown={(e) => {
          if (e.key === 'Enter') handleAddTodo();
        }}
        placeholder="add a new todo"
      />

      <ul>
        {todos.map((todo) => (
          <li key={todo.id} onClick={() => handleRemoveTodo(todo.id)}>
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

¿Qué habilidades y conceptos clave se practican?

Este flujo integra desde la configuración del store hasta el renderizado de la lista con interacciones del usuario. Refuerza patrones modernos con Redux Toolkit y tipado estricto con TypeScript.

¿Qué keywords y patrones practicar?

  • Reducers, acciones y dispatch: estructura para actualizar el estado global.
  • useDispatch y useSelector: lectura y escritura en el store desde UI.
  • AppDispatch y RootState: tipado de dispatch y del selector.
  • Estado local con useState: manejo del texto del input antes de despachar.
  • Mapeo de emojis: objeto clave-valor con tipo {[key: string]: string}.
  • Normalización: uso de toLowerCase y fallback al texto original.
  • Eventos de teclado y clic: onKeyDown con tecla Enter y onClick para eliminar por ID.
  • Renderizado de listas: uso de map, key única y li interactiva.
  • Provider y store: acceso al estado global desde el árbol de componentes.

¿Qué diferencias se mencionan con useReducer y Substand?

  • useReducer: recomendado para estado local complejo en un solo componente.
  • Redux: pensado para estado global compartido entre múltiples componentes.
  • Substand: se adelanta como alternativa para simplificar acciones y reducers con un enfoque distinto de estado global.

¿Quieres que revisemos tu implementación, nombres de tipos o estructura de carpetas para pulirla? Cuéntame en qué parte te gustaría profundizar.