Aprende a controlar un estado complejo con TypeScript y acciones add/remove usando useReducer. Aquí verás cómo estructurar un reducer claro, tipado y predecible, y cómo crear un emoji map que traduce texto del input a su símil en emoji, manteniendo un flujo de datos limpio y fácil de escalar.
¿Cómo se estructura el reducer con TypeScript y useReducer?
La base es una función reducer que recibe el estado y una acción. Con un switch case en action.type, decides si agregas o eliminas elementos del array todos. La clave: retornar siempre un nuevo estado sin mutarlo.
- Usa tipos explícitos para Todo, State y Action.
- Estructura el reducer con case para
add y remove, y un default que devuelve el estado intacto.
- Mantén la inmutabilidad con el operador
... para hacer copias superficiales.
// Tipos base
type Todo = { id: number; text: string };
type State = { todos: Todo[] };
type Action =
| { type: 'add'; payload: string }
| { type: 'remove'; payload: number };
// Reducer principal
const TodoReducer = (state: State, action: Action): State => {
switch (action.type) {
case 'add': {
const newTodo: Todo = {
id: state.todos.length + 1, // ID incremental basado en el tamaño actual.
text: action.payload, // Texto capturado desde el input vía payload.
};
return { ...state, todos: [...state.todos, newTodo] };
}
case 'remove': {
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload),
};
}
default:
return state; // Acción no reconocida: no cambia el estado.
}
};
¿Cómo se agrega con la acción add?
Cuando llega una action con tipo add, se crea un newTodo con id y text. El texto proviene del payload de la acción que despacha el component que controla el input. El id se calcula a partir del tamaño del array para que sea incremental.
- Se conserva el estado previo con
{ ...state }.
- Se añade el nuevo elemento con
[...state.todos, newTodo].
- Se mantiene la inmutabilidad en cada paso.
¿Cómo se elimina con la acción remove?
Para remove, se retorna un nuevo estado con todos filtrados. La condición usa filter para excluir el elemento cuyo id coincide con el payload de la acción.
filter crea un array nuevo sin mutar el original.
- Se compara
todo.id !== action.payload para decidir qué elemento queda.
- Se respeta el contrato del reducer: entrada → salida determinista.
¿Cómo se manejan estado, destructuración y retorno inmutables?
El patrón es: leer el estado actual, calcular el siguiente y retornar un objeto nuevo. La desestructuración permite copiar propiedades existentes sin perder información.
- Estado inicial con
todos como array de objetos Todo.
- Desestructuración con
...state para mantener propiedades previas.
- Acción y payload: la acción trae
type y payload para decidir y operar sin efectos colaterales.
- switch case: control explícito de flujo para
add y remove.
- default: seguridad al devolver el estado cuando el tipo no coincide.
- Inmutabilidad: uso de
[...array] y filter para evitar mutaciones.
¿Cómo crear un emoji map tipado para el input?
Se define un objeto para mapear palabras ingresadas en el input a su emoji correspondiente. Esto facilita que, al tipear, se muestre el emoji asociado.
- Estructura clave-valor de tipo
string a string.
- Flexible: puedes mapear tantas palabras como necesites.
- Útil para enriquecer la UI sin lógica compleja.
// Mapeo simple palabra → emoji
type EmojiMap = { [key: string]: string };
const emojiMap: EmojiMap = {
it: '🍔',
exercise: '🏃♂️',
};
En tu componente que despacha acciones, podrás leer el input, construir la action con su payload y dejar que el reducer mantenga el estado claro y consistente. ¿Qué otras palabras y emojis te gustaría asociar? Comparte tus ideas en los comentarios.