Construye una Emoji Todo List con React + TypeScript y la librería de Substance conectando un hook de store, gestionando state local y mapeando texto a emojis. Verás cómo agregar con Enter, renderizar la UI y eliminar ítems con un clic, tal como se implementa paso a paso.
¿Cómo conectar el hook use todo store en un React functional component?
Para iniciar, se define el componente como React Functional Component y se consume el hook personalizado use todo store para obtener todos, add todo y remove todo. Así se pinta el estado inicial y se preparan las acciones para modificarlo.
// Dentro de TodoList (React + TypeScript)
const todos = useTodoStore((state) => state.todos);
const addTodo = useTodoStore((state) => state.addTodo);
const removeTodo = useTodoStore((state) => state.removeTodo);
const [todoText, setTodoText] = useState('');
- use todo store: expone todos, add todo y remove todo.
- Estado local con useState: captura y modifica la entrada del input.
- Pintado inicial: se renderiza la lista con el estado del store.
¿Cómo gestionar el estado local del input?
Se crea todoText con useState('') para escribir y limpiar el campo tras cada inserción.
const handleAddTodo = () => {
// mapeo a emoji se aplica aquí más adelante
addTodo(todoText);
setTodoText('');
};
- Captura la entrada del usuario.
- Agrega el todo y limpia el campo.
¿Cómo disparar la acción con enter?
No se usa botón. Se detecta la tecla Enter con un KeyboardEvent y se llama a handleAddTodo.
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') handleAddTodo();
};
- Sin botón: solo teclado.
- Flujo rápido para agregar tareas.
¿Cómo mapear palabras a emojis con TypeScript de forma segura?
Se define un emoji map como objeto clave-valor para transformar palabras como "it" o "sleep" en emojis. Con TypeScript, se tipa como Record<string, string> para validar que tanto la clave como el valor sean string.
const emojiMap: Record<string, string> = {
it: '🍔',
sleep: '🛏️',
exercise: '🏃',
};
const handleAddTodo = () => {
const mappedText = (emojiMap[todoText.toLowerCase()] ?? todoText).trim();
addTodo(mappedText);
setTodoText('');
};
Record<string, string>: tipado claro de clave y valor.
toLowerCase(): tolerancia a mayúsculas del usuario.
?? todoText: si no hay coincidencia, usa el texto original.
trim(): evita espacios innecesarios.
¿Cómo renderizar la UI y conectar eventos en React?
Se define el return con un contenedor, el texto “made with Substance”, el título “Emoji Todo List”, el input controlado y la lista de elementos desde el store. Cada elemento se remueve con onClick pasando su id.
return (
<div>
<p>made with Substance</p>
<h2>Emoji Todo List</h2>
<input
type="text"
value={todoText}
onChange={(e) => setTodoText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Add a new todo"
/>
<ul>
{todos.map((todo) => (
<li key={todo.id} onClick={() => removeTodo(todo.id)}>
{todo.text}
</li>
))}
</ul>
</div>
);
- Input controlado:
value, onChange, placeholder descriptivo.
- Agregar con teclado:
onKeyDown para Enter.
- Lista:
map con key={todo.id} y onClick para eliminar.
¿Cómo preparar el punto de entrada y la exportación?
Se cambia el render de App a TodoList en main y se asegura export default del componente.
// main.tsx
createRoot(document.getElementById('root')!).render(<TodoList />);
// TodoList.tsx
export default TodoList;
- Ajuste del entry point a
TodoList.
export default para evitar errores de importación.
- Estilos listos desde
index.css ya preparado.
Además, se probó la interacción escribiendo “it”, “sleep” y “exercise”, confirmando el mapeo a emojis y la eliminación con clic. Se mencionó que este Emoji Todo List se trabajó con Redux y con Substance, comparando diferencias de uso.
¿Con cuál te quedas: Redux o Substance? Cuéntame en comentarios y por qué elegiste esa opción.