En esta ocasión, volveremos a crear formularios dinámicos pero ahora con ayuda de la librería de react-hook-form.
Nota 📣: Necesitas tener conocimiento en Typescript para seguir este tutorial, así como de React JS.
Tal vez te interese este articulo, donde también hacemos lo mismo que en este post, pero usando la librería de Formik. 😉
Formularios dinámicos con Formik y React JS. 📝
Franklin Martinez ・ Oct 28 '22
Tabla de contenido.
📌 Tecnologías a utilizar.
📌 Creando el proyecto.
📌 Primeros pasos.
📌 Creando el objeto de formulario.📌 Creando el tipado para los inputs.
📌 Ahora si, creamos el objeto de formulario con ayuda del tipado.📌 Creando el esquema de validaciones para nuestro formulario.
📌 Función para generar los inputs.
📌 Creando el componente de formulario.
📌 Creando los componentes de cada input.
📌 Usando nuestro componente Form.
📌 Conclusión.
💊 Tecnologías a utilizar.
- React JS 18.2.0
- TypeScript 4.9.3
- React Hook Form 7.43.0
- Vite JS 4.1.0
- Tailwind CSS 3.2.4 (no se muestra el proceso de instalación ni de configuración)
💊 Creando el proyecto.
Al proyecto le colocaremos el nombre de: dynamic-forms-rhf
(opcional, tu le puedes poner el nombre que gustes).
npm create vite@latest
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.
Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.
cd dynamic-forms-rhf
Luego instalamos las dependencias.
npm install
Después abrimos el proyecto en un editor de código (en mi caso VS code).
code .
💊 Primeros pasos.
Dentro del archivo src/App.tsx borramos todo y creamos un componente que muestre un hola mundo
const App = () => {
return <div>Hello world</div>;
};
export default App;
🚨 Nota: Cada vez que creamos una nueva carpeta, también crearemos un archivo index.ts para agrupar y exportar todas las funciones y componentes de otros archivos que están dentro de la misma carpeta, y que dichas funciones puedan ser importadas a traves de una sola referencia a esto se le conoce como archivo barril.
Vamos a crear un layout, creamos una carpeta src/components y dentro creamos un archivo Layout.tsx.
export const Layout = ({ children }: { children: JSX.Element | JSX.Element[] }) => {
return (
<>
<h1 className='text-center my-10 text-5xl'>
<span>Dynamic Form</span>
<span className='font-bold bg-clip-text text-transparent text-[#EC5990]'>
{' - '}
React Hook Form
</span>
</h1>
<main className='grid sm:grid-cols-2 grid-cols-1 sm:mb-0 mb-10 gap-10 place-items-start justify-items-center px-5'>
{children}
</main>
</>
)
}
Ahora, dentro de archivo src/App.tsx, agregamos el layout.
import { Layout } from './components'
const App = () => {
return (
<Layout>
<span>Form</span>
</Layout>
)
}
export default App
Luego vamos a instalar los paquetes necesarios.
- react-hook-form, para manejar los formularios de una manera mas fácil.
- yup, para manejar validaciones en los formularios.
- @hookform/resolvers, para integrar yup con react-hook form.
npm install -E react-hook-form @hookform/resolvers yup
Anteriormente ya había realizado este mismo ejercicio de formularios dinámicos pero usando la librería de Formik, y la verdad es muy similar lo que vamos a realizar, lo único que cambiar son los componentes como el formulario e inputs.
💊 Creando el objeto de formulario.
💊 Creando el tipado para los inputs.
Antes, vamos a crear el tipado. Creamos una nueva carpeta src/types y creamos el archivo index.ts.
Ahora primero creamos la interfaz para los inputs, el cual puede incluso tener más propiedades, pero estas son suficientes para hacer este ejemplo.
Lo mas destacado son las ultimas tres propiedades de la interfaz InputProps:
- typeValue: necesaria ya que necesitamos indicarle a Yup que tipo de valor acepta el input.
-
validations: validaciones que se le establecerán a Yup en base al input; solo coloque validaciones básicas, aunque puedes integrar más si buscas en la documentación de Yup.
- La validación que tal vez se mas se te complique puede ser oneOf, si no haz usado Yup. Esta validación, necesita de una referencia o sea el name de otro input para validar si ambos input contienen el mismo contenido. Un ejemplo de donde usar esta validación es en un input donde creas un password y otro donde tienes que repetir password y ambos valores tienen que coincidir.
- options: esta propiedad es necesario solo si el input es un select o un grupo de inputs de tipo radio
export interface InputProps {
type: 'text' | 'radio' | 'email' | 'password' | 'select' | 'checkbox'
name: string
value: string | number | boolean
placeholder?: string
label?: string
typeValue?: 'boolean' | 'number'
validations?: Validation[]
options?: Opt[]
}
export interface Opt {
value: string | number
desc: string
}
export interface Validation {
type: 'required' | 'isEmail' | 'minLength' | 'isTrue' | 'oneOf'
value?: string | number | boolean
message: string
ref?: string
}
También de una vez creamos este tipo para los tipos de formularios que vamos a desarrollar.
En este caso solo vamos a crear dos formularios.
export type FormSection = 'register' | 'another'
💊 Ahora si, creamos el objeto de formulario con ayuda del tipado.
Gracias a Typescript podemos crear nuestros formularios en este objeto.
Creamos una nueva carpeta src/lib y dentro creamos el archivo form.ts y agregamos lo siguiente:
import { FormSection, InputProps } from '../types';
export const forms: { [K in FormSection]: InputProps[] } =
{
register: [
{
label: "New username",
type: "text",
name: "username",
placeholder: "New username",
value: "",
validations: [
{
type: "minLength",
value: 3,
message: "Min. 3 characters",
},
{
type: "required",
message: "Username is required"
},
],
},
{
label: "New Password",
type: "password",
name: "password",
placeholder: "New password",
value: "",
validations: [
{
type: "required",
message: "Password is required"
},
{
type: "minLength",
value: 5,
message: "Min. 5 characters",
}
],
},
{
label: 'Repeat your password',
type: "password",
name: "repeat_password",
placeholder: "Repeat password",
value: "",
validations: [
{
type: "required",
message: "Repeat password is required"
},
{
type: "minLength",
value: 5,
message: "Min. 5 characters",
},
{
type: 'oneOf',
message: 'Passwords must match',
ref: 'password'
}
],
},
],
another: [
{
label: "E-mail address",
type: "email",
name: "email",
placeholder: "correo@correo.com",
value: "",
validations: [
{
type: "required",
message: "Email is required"
},
{
type: "isEmail",
message: "Email no valid"
}
],
},
{
type: "select",
name: "rol",
label: "Select an option: ",
value: "",
options: [
{
value: "admin",
desc: "Admin",
},
{
value: "user",
desc: "User"
},
{
value: "super-admin",
desc: "Super Admin"
}
],
validations: [
{
type: "required",
message: "Rol is required"
}
]
},
{
type: "radio",
name: "gender",
label: "Gender: ",
value: "",
options: [
{
value: 'man',
desc: "Man"
},
{
value: "woman",
desc: "Woman"
},
{
value: "other",
desc: "Other"
},
],
validations: [
{
type: "required",
message: "Gender is required"
}
]
},
{
type: "checkbox",
name: "terms",
typeValue: "boolean",
label: "Terms and Conditions",
value: false,
validations: [
{
type: "isTrue",
message: "Accept the terms!"
}
]
},
]
}
💊 Creando el esquema de validaciones para nuestro formulario.
Vamos a crear un nuevo archivo en src/lib y lo nombramos getInputs.ts
Creamos una nueva función para generar las validaciones a cada input.
Esta función recibe los campos, y cada campo es de tipo InputProps. También vamos a crear 2 tipos solamente para que Typescript no nos moleste mas adelante.
Nota que creamos los tipos YupBoolean y YupString. Si quieres puedes agregar otros tipos ya sea para manejar algún otro tipo de dato como el numérico o el arreglo. Por ejemplo:
type YupNumber = Yup.NumberSchema<boolean | undefined, AnyObject, number | undefined>
Yo no lo agrego, porque en mis interfaces no manejo alguna validación de tipo numero o arreglo.
import * as Yup from "yup";
import { AnyObject } from "yup/lib/types";
import { FormSection, InputProps } from '../types';
import { forms } from '../lib';
type YupBoolean = Yup.BooleanSchema<boolean | undefined, AnyObject, boolean | undefined>
type YupString = Yup.StringSchema<string | undefined, AnyObject, string | undefined>
const generateValidations = (field: InputProps) => {}
Primero creamos una variable que se inicializara con el tipo de dato que manejara nuestro input. El tipo de dato lo obtenemos de la propiedad typeValue, en caso de que sea undefined por defecto el tipo de dat sera string, y entonces ejecutamos la función
let schema = Yup[field.typeValue || 'string']()
Luego vamos a recorrer las validaciones del campo, ya que es un arreglo.
Dentro del ciclo, usaremos un switch case, evaluando que tipo de regla es la que tiene dicho campo.
const generateValidations = (field: InputProps) => {
let schema = Yup[field.typeValue || 'string']()
for (const rule of field.validations) {
switch (rule.type) { }
}
}
En cada caso del switch vamos a sobrescribir la variable schema. De la siguiente manera:
Si tiene una validación 'isTrue' significa que el input maneja valores booleanos, por lo cual queremos que nuestro schema se comporte como un YupBoolean, ya que si no Typescript se estaría quejando. Luego ejecutamos la función que tenga que ver con cada caso.
Por ejemplo, en el caso de 'isTrue', ejecutamos la función que se llama exactamente igual, y dentro le pasamos el mensaje
case 'isTrue' : schema = (schema as YupBoolean).isTrue(rule.message); break;
En el caso de que la validación sea oneOf, necesitamos enviarle, como primer parámetro un arreglo y como segundo parámetro un mensaje.
En el caso del arreglo, debe ser el valor con el que quieres que coincida, pero en este caso queremos que coincida con el valor de otro campo, por eso usamos Yup.ref el cual necesita un string que hace referencia al atributo name de un input.
Para que asi cuando se haga la validación, se verifique si ambos campos contienen el mismo valor.
case 'oneOf' : schema = (schema as YupString)
.oneOf(
[ Yup.ref(rule.ref as string) ],
rule.message
);
break;
Asi quedaría nuestra primera función. Al final retornamos la variable schema.
Nota que al inicio de la función, colocamos una condición donde si el campo no tiene validaciones entonces retornar null y evitar ejecutar el ciclo.
import * as Yup from "yup";
import { AnyObject } from "yup/lib/types";
import { FormSection, InputProps } from '../types';
import { forms } from '../lib';
type YupBoolean = Yup.BooleanSchema<boolean | undefined, AnyObject, boolean | undefined>
type YupString = Yup.StringSchema<string | undefined, AnyObject, string | undefined>
const generateValidations = (field: InputProps) => {
if (!field.validations) return null
let schema = Yup[field.typeValue || 'string']()
for (const rule of field.validations) {
switch (rule.type) {
case 'isTrue' : schema = (schema as YupBoolean).isTrue(rule.message); break;
case 'isEmail' : schema = (schema as YupString).email(rule.message); break;
case 'minLength': schema = (schema as YupString).min(rule?.value as number, rule.message); break;
case 'oneOf' : schema = (schema as YupString).oneOf([Yup.ref((rule as any).ref)], rule.message); break;
default : schema = schema.required(rule.message); break;
}
}
return schema
}
💊 Función para generar los inputs.
Primero vamos a crear un función y la nombramos getInputs, la cual es de tipo genérico y recibe como parámetro la section (o sea que formulario quieres obtener sus campos, en este caso puede ser el formulario de signUp o el otro).
Vamos a crear dos variables que las inicializaremos como objetos vacíos y que al final deberán contener nuevas propiedades.
export const getInputs = <T>(section: FormSection) => {
let initialValues: { [key: string]: any } = {};
let validationsFields: { [key: string]: any } = {};
};
Dentro de la función haremos un ciclo for of. En el caul vamos a recorrer los campos de un formulario en especifico.
Dentro del ciclo, vamos a computar los valores en la variable de initialValues, y para computar los valores usamos la propiedad name del campo.
-
Verificamos si existen validaciones para el campo.
- Si no hay validaciones, entonces seguir con el siguiente campo.
- Si hat validaciones, ejecutamos la función que creamos anteriormente generateValidations mandando el campo como argumento.
Luego a la variable validationsFields, también le computamos los valores usando la propiedad name del campo, y le asignamos el schema de validación que se ha generado.
for (const field of forms[section]) {
initialValues[field.name] = field.value;
if (!field.validations) continue;
const schema = generateValidations(field)
validationsFields[field.name] = schema;
}
Una vez terminado el ciclo, debemos retornar 3 propiedades.
- El esquema de validación dentro de un Yup.object, esparciendo las propiedades de validationsFields. ```tsx
validationSchema: Yup.object({ ...validationsFields }),
- Los valores iniciales, y haremos que se comporten como genérico para poder usarlos después
```tsx
initialValues: initialValues as T,
- Los campos que queremos mostrar en nuestro formulario.
inputs: forms[section]
Asi se vería al final nuestra función
export const getInputs = <T>(section: FormSection) => {
let initialValues: { [key: string]: any } = {};
let validationsFields: { [key: string]: any } = {};
for (const field of forms[section]) {
initialValues[field.name] = field.value;
if (!field.validations) continue;
const schema = generateValidations(field)
validationsFields[field.name] = schema;
}
return {
validationSchema: Yup.object({ ...validationsFields }),
initialValues: initialValues as T,
inputs: forms[section],
};
};
💊 Creando el componente de formulario.
Primero vamos a preparar la interfaz para las props que va a recibir nuestro componente Form.
- onSubmit, función que ejecuta el formulario.
- labelButtonSubmit, texto que mostrara el botón.
- titleForm, texto que mostrara el formulario.
Los ultimas 3 propiedades son lo que regresa la función que hicimos para generar los inputs y sus validaciones.
interface Props {
onSubmit: (data: unknown) => void
labelButtonSubmit?: string
titleForm?: string
initialValues: unknown
validationSchema: SchemaForm
inputs: InputProps[]
}
La propiedad validationSchema es de tipo SchemaForm.
// src/types/index.ts
export type SchemaForm = OptionalObjectSchema<{
[x: string]: any;
}, AnyObject, TypeOfShape<{
[x: string]: any;
}>>
Ahora creamos el componente, y dentro desestructuramos las props que recibe el componente.
Luego usamos el hook de useForm, el cual vamos a establecer un objeto como argumento, accedemos a la propiedad:
- resolver, para establecer el esquema de validación, para ello usamos la función yupResolver y le pasamos como argumento el validationSchema que viene por props.
- defaultValues, para establecer los valores por defecto y le asignaremos la props de initialValues.
Toma en cuenta que no desestructuramos nada del hook useForm.
import { yupResolver } from '@hookform/resolvers/yup'
import { useForm } from 'react-hook-form'
export const Form = ({ ...props }: Props) => {
const {
initialValues,
inputs,
onSubmit,
validationSchema,
titleForm,
labelButtonSubmit = 'Submit'
} = props
const formMethods = useForm({
resolver: yupResolver(validationSchema),
defaultValues: { ...(initialValues as any) }
})
return (
<></>
)
}
Después, vamos a usar un componente que nos ofrece react-hook-form, que es el FormProvider y le vamos a esparcir los formMethods del hook useForm.
El FormProvider nos ayudara a comunicar el estado del formulario con los componentes (inputs) que estén anidados dentro del FormProvider. Con el propósito de separar los componentes y no tener todo en un mismo archivo.
Dentro del FormProvider colocaremos un form y en el método onSubmit de la etiqueta form, vamos a ejecutar una propiedad del formMethods, que es el handleSubmit, y como argumento le pasamos el onSubmit que recibe el componente Form por props.
Este handleSubmit, solo se va a ejecutar si no hay errores en cada input, y cuando se ejecute nos devolverá los valores de cada input.
import { FormProvider, useForm } from 'react-hook-form'
// interface
export const Form = ({ ...props }: Props) => {
// props
const formMethods = useForm({
resolver: yupResolver(validationSchema),
defaultValues: { ...(initialValues as any) }
})
return (
<FormProvider {...formMethods}>
<form
onSubmit={formMethods.handleSubmit(onSubmit)}
className='bg-secondary rounded-md p-10 pt-5 shadow-2xl shadow-primary/30 flex flex-col gap-2 border border-primary w-full min-h-[390px]'
>
<section className='flex-1 flex flex-col gap-3'>
{/* inputs here */}
</section>
</form>
</FormProvider>
)
}
Ahora vamos a crear una función, para retornar los diferentes tipos de inputs.
Usamos la prop inputs que desestructuramos de las props que recibe el componente Form.
En base al tipo de input vamos a renderizar un uno u otro input.
Nota que estamos usando componentes que aun no hemos creado. También nota, que de las propiedades de cada input, vamos a excluir las validations, typeValue y value, porque son valores que no necesita nuestro input directamente.
Una cosa a mejorar sobre esta función, es que puedes crear un componente aparte, y crear un diccionario con los componentes y el tipo de input.
En este caso no lo hago, para no extenderme más.
const createInputs = () =>
inputs.map(({ validations, typeValue, value, ...inputProps }) => {
switch (inputProps.type) {
case 'select':
return <CustomSelect {...inputProps} key={inputProps.name} />
case 'checkbox':
return <CustomCheckbox {...inputProps} key={inputProps.name} />
case 'radio':
return <CustomRadio {...inputProps} key={inputProps.name} />
default:
return <CustomInput {...inputProps} key={inputProps.name} />
}
})
Finalmente, ejecutamos la función createInputs dentro de la etiqueta section. E inmediatamente vamos a crear los custom inputs.
// imports
// interface
export const Form = ({ ...props }: Props) => {
// props
const formMethods = useForm({
resolver: yupResolver(validationSchema),
defaultValues: { ...(initialValues as any) }
})
const createInputs = () =>
inputs.map(({ validations, typeValue, value, ...inputProps }) => {
switch (inputProps.type) {
case 'select':
return <CustomSelect {...inputProps} key={inputProps.name} />
case 'checkbox':
return <CustomCheckbox {...inputProps} key={inputProps.name} />
case 'radio':
return <CustomRadio {...inputProps} key={inputProps.name} />
default:
return <CustomInput {...inputProps} key={inputProps.name} />
}
})
return (
<FormProvider {...formMethods}>
<form
onSubmit={formMethods.handleSubmit(onSubmit)}
className='bg-secondary rounded-md p-10 pt-5 shadow-2xl shadow-primary/30 flex flex-col gap-2 border border-primary w-full min-h-[390px]'
>
<section className='flex-1 flex flex-col gap-3'>
{ createInputs() }
</section>
</form>
</FormProvider>
)
}
💊 Creando los componentes de cada input.
Primero, vamos a crear un mensaje de error, que se va a mostrar cada que la validación del input falle.
Dentro de src/components creamos ErrorMessage.tsx
interface Props { error?: string }
export const ErrorMessage = ({ error }: Props) => {
if (!error) return null
return (
<div className='w-full grid place-content-end'>
<p className='text-red-400 text-sm'>{error}</p>
</div>
)
}
Ahora, vamos a crear una nueva carpeta src/components/inputs y dentro crearemos 4 archivos.
Estos cuatro componentes que vamos a crear reciben props que son de tipo CustomInputProps. Puedes colocarlo en el archivo src/types/index.ts
export type CustomInputProps = Omit<InputProps, 'validations' | 'typeValue' | 'value'>
Y ademas como cada input que crearemos estará dentro de un FormProvider, podemos utilizar otro custom hook de react-hook-form, el cual es useFormContext, este hook nos ayudara a conectar el estado del formulario con el input.
- CustomGenericInput.tsx
De useFormContext, obtenemos el la propiedad register, y la propiedad errors dentro del formState.
const {
register,
formState: { errors }
} = useFormContext()
Creamos el error, computando el objeto de errors con la prop name que recibe el componente y obtenemos el mensaje.
const error = errors[name]?.message as string | undefined
Al momento de construir el input, necesitamos esparcir las propiedades de la función register, el cual tenemos que pasar le la prop name para que react-hook-form identifique que errores y validaciones debe tener este input.
Luego le esparcimos las demás propiedades en caso de que tenga más (como por ejemplo el placeholder).
<input
className='py-1 px-2 rounded w-full text-black'
{...register(name)}
{...props}
id={id}
/>
Asi quedaría al final este componente.
import { useFormContext } from 'react-hook-form'
import { ErrorMessage } from '../../components'
import { CustomInputProps } from '../../types'
export const CustomInput = ({ name, label, ...props }: CustomInputProps) => {
const {
register,
formState: { errors }
} = useFormContext()
const error = errors[name]?.message as string | undefined
const id = `${name}-${props.type}-${label}`
return (
<div className='w-full flex gap-1 flex-col'>
{label && (
<label className='text-white text-sm' htmlFor={id}>
{label}
</label>
)}
<input
className='py-1 px-2 rounded w-full text-black'
{...register(name)}
{...props}
id={id}
/>
<ErrorMessage error={error} />
</div>
)
}
- CustomCheckbox.tsx
import { useFormContext } from 'react-hook-form'
import { ErrorMessage } from '../../components'
import { CustomInputProps } from '../../types'
export const CustomCheckbox = ({ name, label, ...props }: CustomInputProps) => {
const {
register,
formState: { errors }
} = useFormContext()
const error = errors[name]?.message as string | undefined
return (
<div>
<label className='flex gap-2 items-center cursor-pointer w-fit'>
<input {...props} {...register(name)} />
{label}
</label>
<ErrorMessage error={error} />
</div>
)
}
- CustomSelect.tsx
Este input es casi igual que todos los demás, solo que aquí tenemos la prop options donde vendrán los valores del select que se pueden seleccionar.
import { useFormContext } from 'react-hook-form'
import { ErrorMessage } from '../../components'
import { CustomInputProps } from '../../types'
export const CustomSelect = ({ name, label, options, ...props }: CustomInputProps) => {
const {
register,
formState: { errors }
} = useFormContext()
const error = errors[name]?.message as string | undefined
const id = `${name}-${props.type}-${label}`
return (
<div className='flex flex-col gap-2'>
<div className='flex items-center gap-4'>
<label htmlFor={id}>{label}</label>
<select {...register(name)} {...props} id={id} className='p-2 rounded flex-1 text-black'>
<option value=''>--- Select option ---</option>
{options &&
options.map(({ desc, value }) => (
<option key={value} value={value}>
{desc}
</option>
))}
</select>
</div>
<ErrorMessage error={error} />
</div>
)
}
- CustomRadioGroup
Muy parecido al CustomSelect.tsx. Solo que aquí renderizamos un input de tipo radio.
import { useFormContext } from 'react-hook-form'
import { ErrorMessage } from '../../components'
import { CustomInputProps } from '../../types'
export const CustomRadio = ({ name, label, options, ...props }: CustomInputProps) => {
const {
register,
formState: { errors }
} = useFormContext()
const error = errors[name]?.message as string | undefined
return (
<div className='flex flex-col'>
<div className='flex items-center gap-4'>
<label>{label}</label>
<section className='flex justify-between flex-1'>
{options &&
options.map(({ desc, value }) => (
<label
key={value}
className='flex items-center gap-1 cursor-pointer hover:underline rounded p-1'
>
<input {...register(name)} {...props} value={value} type='radio' />
{desc}
</label>
))}
</section>
</div>
<ErrorMessage error={error} />
</div>
)
}
💊 Usando nuestro componente Form.
Ahora vamos al archivo src/App.tsx
Para usar el componente Form.
Tenemos que ejecutar la función getInputs y obtener las validaciones, valores iniciales e inputs. Lo haremos fuera del componente. También creamos una interfaz para que los valores iniciales se comporten como dicha interfaz.
interface SignUpFormType {
username: string
password: string
repeat_password: string
}
const signUpForm = getInputs<SignUpFormType>('register')
Luego importamos el componente Form, le esparcimos las propiedades que nos regresa getInput. Y también le pasamos las demás props.
import { Layout, Form } from './components'
import { getInputs } from './lib'
interface SignUpFormType {
username: string
password: string
repeat_password: string
}
const signUpForm = getInputs<SignUpFormType>('register')
const App = () => {
const onSubmitSignUp = (data: unknown) => console.log({ singUp: data })
return (
<Layout>
<Form
{...signUpForm}
onSubmit={onSubmitSignUp}
titleForm='Sign Up!'
labelButtonSubmit='Create account'
/>
</Layout>
)
}
export default App
En el caso de que quieras sobrescribir los valores iniciales, solamente te creas una nueva constante esparciendo los valores iniciales y luego sobrescribiendo lo que necesites. Para luego pasar le un nuevo valor a la prop de initialValues.
const App = () => {
const onSubmitSignUp = (data: unknown) => console.log({ singUp: data })
const initialValuesSignUp: SignUpFormType = {
...signUpForm.initialValues,
username: '@franklin361'
}
return (
<Layout>
<Form
{...signUpForm}
initialValues={initialValuesSignUp}
onSubmit={onSubmitSignUp}
titleForm='Sign Up!'
labelButtonSubmit='Create account'
/>
</Layout>
)
}
export default App
Y asi mismo puedes incluir varios formularios de manera dinámica.
import { Layout, Form } from './components'
import { getInputs } from './lib'
interface SignUpFormType {
username: string
password: string
repeat_password: string
}
interface AnotherFormType {}
const signUpForm = getInputs<SignUpFormType>('register')
const anotherForm = getInputs<AnotherFormType>('another')
const App = () => {
const onSubmitSignUp = (data: unknown) => console.log({ singUp: data })
const onSubmitAnotherForm = (data: unknown) => console.log({ another: data })
const initialValuesSignUp: SignUpFormType = {
...signUpForm.initialValues,
username: '@franklin361'
}
return (
<Layout>
<Form
{...signUpForm}
initialValues={initialValuesSignUp}
titleForm='Sign Up!'
onSubmit={onSubmitSignUp}
labelButtonSubmit='Create account'
/>
<Form
{...anotherForm}
titleForm='Another form!'
onSubmit={onSubmitAnotherForm}
labelButtonSubmit='Send info'
/>
</Layout>
)
}
export default App
💊 Conclusión.
React Hook Form es una de mis librearas favoritas, ya que tiene ciertas ventajas sobre otras librerías populares como por ejemplo Formik; como por ejemplo el bundle size es mas pequeño, tiene menos dependencias, produce menos re-renders, etc. 😉
Pero de igual manera ambas son librerías muy usadas.
Espero que te haya gustado esta publicación y que también espero haberte ayudado a entender como realizar formularios dinámicos usando React Hook Form. 🙌
Si conoces alguna otra forma distinta o mejor de realizar esta aplicación con gusto puedes comentarla.
Te invito a que revises mi portafolio en caso de que estés interesado en contactarme para algún proyecto! Franklin Martinez Lucas
🔵 No olvides seguirme también en twitter: @Frankomtz361
💊 Demostración simple.
https://dynamic-form-rhf.netlify.app/
Top comments (1)
Me agrado este post, muy util, gracias!