Contenido del curso

Texto por defecto en textarea con useLocation

Resumen

Mejorar la user experience en una aplicación React no siempre significa rediseñar interfaces: a veces basta con que un textarea muestre el texto correcto al editar. Aquí aprenderás a usar useLocation de React Router DOM v6 junto con custom hooks para resolver estados de carga y transmitir información entre rutas en tu todo app.

¿Por qué el textarea aparece vacío al editar un todo?

Cuando un usuario hace clic en el botón de editar, navega a la ruta /edit/:id, pero el textarea aparece vacío. Eso obliga a reescribir todo el contenido desde cero, una experiencia frustrante que rompe la confianza en la app [01:00].

La solución empieza por enviar un valor por defecto al TodoForm desde EditTodoPage. Esa propiedad puede llamarse defaultTodoText y debe contener el texto del todo que el usuario quiere modificar.

¿Qué es defaultTodoText? Es una prop que recibe el componente TodoForm con el texto inicial que aparecerá en el textarea al editar un todo.

¿Cómo crear una función getTodo dentro de un custom hook?

La lógica de buscar un todo específico debe vivir dentro del custom hook useTodos, no dispersa en los componentes. Así mantienes la separación de responsabilidades y reutilizas el comportamiento desde cualquier ruta [02:30].

La función getTodo recibe un id como argumento y retorna el todo que coincida usando todoIndex. A diferencia de addTodo, editTodo o deleteTodo, esta función no modifica el estado: solo lo lee. Por eso se expone desde state y no desde stateUpdaters, aunque técnicamente sea una función.

¿Dónde ubicar funciones que leen pero no modifican el estado?

Es una decisión de diseño interesante. stateUpdaters agrupa actualizadores; state agrupa valores. Una función de lectura encaja mejor en state porque expone información derivada, no acciones que cambien la realidad.

¿Por qué getTodo retorna undefined al cargar la página?

Después de implementar getTodo, aparece un error: Cannot read properties of undefined (reading 'text'). La causa está en useLocalStorage, que simula comportamiento asíncrono con un setTimeout de tres segundos [06:45].

El initialValue arranca como un array vacío. Mientras useLocalStorage no termine de cargar, getTodo busca dentro de una lista sin elementos y retorna undefined. La solución pasa por respetar el estado loading que ya retorna el custom hook.

¿Qué hace useLocalStorage en este caso? Simula una llamada a una API real, retornando un estado loading mientras los datos se hidratan desde el almacenamiento local con un retraso artificial.

¿Cómo manejar el estado loading antes de renderizar?

En EditTodoPage se evalúa primero si loading es true. Si lo es, se retorna un párrafo con cargando... como loading skeleton. Cuando loading cambia a false, React vuelve a renderizar y ya hay datos disponibles para llamar a getTodo(id) y obtener todo.text.

Este patrón de guard clause evita que la app intente leer propiedades de objetos que aún no existen.

¿Cómo usar useLocation para transmitir datos entre rutas?

Incluso con el estado de carga resuelto, queda un problema de UX: si el home ya tiene los todos cargados, esperar otros tres segundos al editar es absurdo. Aquí entra useLocation, un custom hook de React Router DOM v6 que permite recibir información transmitida desde otra ruta [13:20].

Desde el home, la función navigate acepta un segundo argumento con una propiedad state. Ahí se envía el todo completo:

js navigate(/edit/${todo.id}, { state: { todo: todo, }, });

En EditTodoPage, useLocation() expone ese estado a través de location.state.todo. Si existe, el texto está disponible al instante, sin esperar la carga.

¿Qué pasa si el usuario recarga la página directamente?

Cuando alguien entra por URL directa o recarga, location.state será undefined porque no hubo navegación previa. Para evitar errores, se usa el operador de encadenamiento opcional ?.:

js let todoText = ''; if (location.state?.todo) { todoText = location.state.todo.text; } else if (loading) { return <p>cargando...</p>; } else { todoText = todo.text; }

Esta cascada cubre tres escenarios: navegación con datos transmitidos, recarga directa con estado de carga, y recarga directa con datos ya disponibles desde getTodo.

¿Qué es useLocation en React Router DOM v6? Es un hook que devuelve el objeto location actual, incluyendo state, donde se puede leer información transmitida por navigate desde otra ruta.

¿Cómo se conecta defaultTodoText con el textarea?

Dentro de TodoForm, el estado del textarea se inicializa con React.useState. En lugar de arrancar siempre con un string vacío, se evalúa la prop recibida:

js const [newTodoValue, setNewTodoValue] = React.useState( props.defaultTodoText || '' );

Si defaultTodoText viene con contenido, se usa como valor inicial. Si no, queda vacío para no romper el componente cuando se reutiliza al crear un todo nuevo.

El resultado es una experiencia fluida: al editar desde el home, el texto aparece de inmediato; al recargar, se muestra brevemente el estado de carga y luego el texto. Sin pantallas vacías ni reescrituras forzadas.

¿Cómo organizarías tú la lógica entre state y stateUpdaters dentro del custom hook? Cuéntalo en los comentarios.