Manejo de formularios en React Router
Con esto empiezo una serie de artículos sobre React Router
En React Router (RR) en su versión 7+ (framework mode o como anteriormente se llamaba, Remix) el manejo de formularios para la mutación, es decir editar o crear registros nuevos o más que nada para leer información introducida por el usuario, es extremadamente sencillo. Esto se debe a que el framework nació con la filosofía de recuperar los estándares web. Crear un formulario es tan fácil como usar etiquetas HTML, pero respetando la sintaxis JSX/TSX de React.
// routes/sign-in.tsx
<form>
<label htmlFor="username">Usuario</label>
<input id="username" type="text" name="username" />
<label htmlFor="username">Contraseña</label>
<input id="password" type="password" name="password" />
<button>Entrar</button>
</form>
⚠️ Ojo: Aunque puedes usar la etiqueta HTML
<form>, esta recargará la página. Lo recomendable es usar el componente<Form>que ofrece React Router. Internamente usa la etiqueta HTML, pero intercepta el envío para hacerlo asíncrono (sin recargar), ofreciendo una experiencia de usuario mucho mejor., este tiene aun muchas más ventajas, de esto también hablaré en otra publicación entre las ventajas o porque es preferible usar las funciones o componentes que nos ofrece RR.
Así de fácil es por ejemplo crear un formulario para iniciar sesión y gracias a la facilidad de usar un framework de react que lo que seria siendo react router (usare este nombre ya que con la próxima version de remix v3, creo ya no tendrá sentido seguir llamándolo remix aunque inicialmente todo inicio con remix, pero no confundir el apartado de routing que también es rect router, pero en próximos artículos hablare mas en concreto sobre react router).
Ahora bien, aprovechando las ventajas del SSR que ofrece react router como framework, la forma de leer esos datos del “frontend” es mediante una función que se exporta, esta función que quizá ya conoces, es action (para el siguiente ejemplo se usara tsx).
// routes/sign-in.tsx
import type { Route } from './+types/sign-in' //aqui depende del archivo que te encuentres y como tengas configurado el leer los tipos
import { redirect } from 'react-router' // usamos el redict de RR pero igual podemos hacerlo manualmente (es un Response con status 301)
export async function action({ request }: Route.ActionArgs) {
// esto se ejecuta del lado del servidor y leemos el form data de request
// el form data seria la información que envía un formulario
const data = await request.formData() // esperamos ya que es asincrono
const username = data.get('username') // viene del input name
const password = data.get('password')
// importante retornar algo o en su defecto retornar null
// en este caso aplicamos una validación
if (!username || !password) {
return { error: 'Datos incorectos' }
}
return redirect('/home') // aquí metemos un redirect si todo sale bien
}
En esta función puedes hacer todas las validaciones y/o llamadas que necesites ya sea directo a la base de datos o a una api y para leer el valor que regresa el action en caso es usando el hook de RR llamado useActionData (hay otro forma que se mostrará a continuación).
Por lo que teniendo el teniendo el ejemplo completo terminaría siendo así:
// routes/sign-in.tsx
import type { Route } from './+types/sign-in'
import { Form, redirect, useActionData } from 'react-router'
export async function action({ request }: Route.ActionArgs) {
const data = await request.formData()
const username = data.get('username')
const password = data.get('password')
if (!username || !password) {
return { error: 'Datos incorrectos' }
}
return redirect('/home')
}
export default function SignIn({ actionData }: Route.ComponentProps) {
return (
<Form method="post">
{actionData && <p>{actionData.error}</p>}
<label htmlFor="username">Usuario</label>
<input id="username" type="text" name="username" />
<label htmlFor="username">Contraseña</label>
<input id="password" type="password" name="password" />
<button type="submit">Entrar</button>
</Form>
)
}
Como ves, ya no necesitas crear inputs controlados, manejar estados locales para el formulario ni usar onSubmit. React Router se encarga de todo. Esto es solo un ejemplo básico; en próximos artículos hablaré más a profundidad de esto, veremos cómo integrar validaciones más robustas, algo indispensable en aplicaciones reales.