Una de las herramientas nuevas que acompañan a React 19 son las nuevas formActions de React. 🔥
Las mutaciones en React son un tema que todo mundo ha estado intentando resolver desde muchas trincheras. Frameworks como Next o React Router ofrecen su propia filosofía al respecto y algunos otros proyectos independientes, como TanStack Query, intentan también ofrecer una solución que haga sentido. Con todo, React no se había interesado en este problema pues se consideraba solo una biblioteca para las vistas en el cliente, sin embargo, con sus nuevos componentes de Servidor, React ha cambiado de ser una animal terrestre en el cliente a convertirse en uno que vive en las profundidades del océano. Es decir, que ahora que React se usa desde el servidor, también nos incluye herramientas para trabajar con mutaciones. Parece que entre todos quieren matar a Redux. Me parece muy poético que Dan Abramov fuera contratado por haber resuelto a su manera el problema de las mutaciones, para intentar resolverlo dentro de React. Por eso tiene sentido que las nuevas React Form Actions eliminen la necesidad de mantener un estado en el cliente. Y también por eso, en esta entrada te voy a explicar en qué consisten y cómo se usan las React Form Actions. ¿Te late? ¡Pues vamos! 🤓
onSubmit
La primera diferencia entre pasar una función al prop onSubmit
de un componente <form>
es que no tenemos que prevenir el refresh de la página por default: e.preventDefault()
.
Lo segundo, que las acciones son tratadas como transiciones (pronto escribiré más sobre transiciones), lo que te permite crear muchas señales de feedback al usuario.
También tenemos acceso a un estado asociado al formulario con el hook useFormStatus
.
Por último, React, ahora puede manejar los errores y race-conditions por nosotros, para confiar plenamente en el estado del formulario y ofrecer una experiencia de carga correcta de pending states.
function Subscription() { function subscribe(formData) { const response = await fetch('/join', { method: 'post', body: formData, }) if (response.ok) { toast.success(response.message) } else { toast.error(response.message) } } const Fields = () => { const { pending } = useFormStatus() return ( <> <input name="email" type="email" /> <button type="submit"> { pending ? 'Cargando...' : "Suscríbete a Fixtergeek.com 🤓" } </button> </> ); } return ( <form action={subscribe}> <Fields /> </form> ) }
El formulario actuará como si fuese un proveedor de contexto (<Context.Provider>
) y cualquiera de sus hijos serán capaces de usar useFormStatus
para enterarse del pending state que se ha vuelto sumamente importante en los últimos años. Puedes también acceder a más datos del formulario como el valor de cada uno de sus campos, tienes acceso a todo el formData
, formato nativo de la web que también se va recuperando. 🤩
Pero este pattern no es el único que nos ofrece React 19 para trabajar con mutaciones, también tenemos otro mejor, pues es más eficiente y renderiza menos.
useActionState
En este otro ejemplo, el hook useActionState
nos va a permitir tener todas las piezas en un solo lugar; hasta nos va a proveer de un estado que podemos usar para tomar decisiones dentro de nuestras interfaces JSX.
async function subscribe( previousState: { joined: boolean }, formData: FormData, ) { const response = await fetch('/join', { method: 'POST', body: formData, }) if (response.ok) { return { subscribed: true } // state } else { return { subscribed: false } } } function SubscriptionWidget() { const [ state, formAction, // Este también es un formAction, pero conectado al estado. isPending ] = useActionState( subscribe, // formAction o action o acción o acción del formulario 🤷🏼♀️ { subscribed: false }, // estado inicial '/join', // permalink like an id ) return ( <div> {state.subscribed ? ( <p>¡Te hemos enviado toda la info a tu correo!</p> ) : ( <form action={formAction}> <button type="submit"> {isPending ? 'Enviando...' : 'Suscríbete a Fixtergeek.com 🤖'} </button> </form> )} </div> ) }
Lo que observamos en este ejemplo el call de useActionState
que va a ser invocada, recibiendo primero una función que será nuestra formAction
; un estado inicial o previo y un permalink. Este hook nos va a devolver entonces tres herramientas dentro de un array. Estas herramientas son primero: el estado reactivo que podemos usar en el JSX; luego nos devolverá el formAction
que debemos darle al formulario junto con un booleano isPending que nos permitirá mostrar estados de carga en cada paso. ¿Todo en un solo lugar, genial no? 🤯
Pero ese no es el único hook que tenemos ahora a nuestra disposición.
useOptimistic
El optimistic UI se está convirtiendo poco a poco en el pattern de las aplicaciones con mejor experiencia de usuario al eliminar las cargas en la experiencia del usuario. Por eso, React ahora nos ofrece este nuevo hook para poder eliminar por completo los spinners. 🪫
function Subscription() { const [state, formAction] = useActionState( subscribe, { subscribed: false }, '/join', ) const [optimisticIsSubscribed, setOptimisticIsSubscribed] = useOptimistic(state.subscribed) return ( <div> {state.subscribe ? ( // Podemos mostrar que aún estamos trabajando... <p> ¡Te lo hemos enviado todo! </p> ) : ( <form action={(formData) => { // Necesitamos interceptar con una función anónima setOptimisticIsSubscribed(true) // Actualizamos optimísticamente await formAction(formData) }} > {optimisticIsSubscribed && <span>Te estamos gegistrando...</span>} <button disabed={optimisticIsSubscribed} type="submit"> Súbete al nuevo curso de Fixtergeek.com 🤩 </button> </form> )} </div> ) }
Trabajar con estados optimistas puede ser un poquito confuso y resultar en muchas pequeñas piezas que mantener. Pero, definitivamente, ofrecerá siempre una experiencia más dinámica y placentera para el usuario. Ahora que tendremos estas nuevas herramientas en React 19 seguro que podrás explorar todos estos patterns en tus nuevos proyectos. Así como espero que esta pequeña entrada te sea de utilidad.
Abrazo. Bliss. 🤓
© 2016 - 2023 Fixtergeek