Reutilizando formularios en React

Hola

Tenemos un panel de administración y muchos formularios en BCS, pero en la comunidad React no existe un método generalmente aceptado: cómo diseñarlos para su reutilización. La guía oficial de Facebook no tiene información detallada sobre cómo trabajar con formularios en condiciones reales, donde se necesita validación y reutilización. Alguien usa forma redux, formik, forma final o incluso escribe su propia solución.


En este artículo mostraremos una de las opciones para trabajar con formularios en React. Nuestra pila será así: Reaccionar + formik + mecanografiado. Mostraremos:

  • Lo que debe hacer un componente.
  • Configuración, campos y validación a nivel de accesorios.
  • Cómo hacer que un formulario sea reutilizable.
  • Optimización del renderizador.
  • Que nuestro método es inconveniente.

Con la nueva tarea comercial, aprendimos que tendremos que hacer entre 15 y 20 formas similares, e hipotéticamente puede haber incluso más. Teníamos un formulario de dinosaurio en la configuración, que funcionaba con datos de la `tienda`, enviaba acciones para guardar y ejecutar solicitudes a través de` sagas`. Ella era maravillosa, haciendo valor comercial. Pero ya no era expandible ni reutilizable, solo con un código deficiente y la adición de muletas.

La tarea es: reescribir el formulario para que pueda reutilizarse un número ilimitado de veces. Bueno, recordamos la programación funcional, tiene funciones puras que no usan datos externos, en nuestro caso `redux`, solo lo que se envían en argumentos (props).

Y eso es lo que pasó.

La idea de nuestro componente es crear un contenedor (contenedor) y escribir en él la lógica de trabajar con el mundo exterior (recibir datos de la tienda Redux y enviar acciones). Para esto, el componente contenedor debe poder recibir información a través de las devoluciones de llamada. La lista completa de accesorios de formulario:

interface IFormProps { //          IsSubmitting?: boolean; //     submitText?: string; //    resetText?: string; //       (  ) validateOnChange?: boolean; //     blur'e  (  ) validateOnBlur?: boolean; // ,      . config: IFieldsFormMetaModel[]; //  . fields: FormFields; //    validationSchema: Yup.MidexSchema; //     onSubmit?: () => void; //     reset  onReset?: (e: React.MouseEvent<HTMLElement>) => void; //    onChangeField?: ( e: React.SyntaticEvent<HTMLInputElement, name: string; value: string ) => void; //      +    onChangeFields?: (values: FormFields, prop: { isValid }) => void; } 

Usando Formik


Usamos el componente <Formik />.

 render() { const { fields, validationSchema, validateOnBlur = true, validateOnChange = true, } = this.props; return ( <Formik initialValues={fields} render={this.renderForm} onSubmit={this.handleSubmitForm} validationSchema={validationSchema} validateOnBlur={validateOnBlur} validateOnChange={validateOnChange} validate={this.validateFormLevel} /> ); } 

En el caso del formulario `validar`, llamamos al método` this.validateFormLevel`, en el que le damos al componente contenedor la oportunidad de obtener todos los campos modificados y verificar si son válidos.

 private validateFormLevel = (values: FormFields) => { const { onChangeFields, validationSchema } = this.props; if (onChangeFields) { validationSchema .validate(values) .then(() => { onChangeFields(values, { isValid: true }); }) .catch(() => { onChangeFields(values, { isValid: false }); }); } } 

Aquí tenemos que volver a llamar a validación para dejar claro al contenedor si los campos son válidos. Al enviar un formulario, simplemente llamamos a prop `onSubmit`:

 private handleSubmitForm = (): void => { const { onSubmit } = this.props; if (onSubmit) { onSubmit(); } } 

Con los accesorios 1-5, todo debería estar claro. Pasemos a 'config', 'fields' y 'validationSchema'.

Accesorios 'config'


 interface IFieldsFormMetaModel { /**   */ sectionName?: string; sectionDescription?: string; fieldsForm?: Array<{ /**    */ name?: string; //          prop 'fields' /**    checked */ checked?: boolean; /** enum,      */ type?: ElementTypes; /**    */ label?: string; /**    */ helperText?: string; /**      */ required?: boolean; /**      */ disabled?: boolean; /**  -    */ minLength?: number; /**            */ initialValue?: IInitialValue; /**      */ selectItems?: ISelectItems[]; //   select, dropdown   }>; } 

En base a esta interfaz, creamos una matriz de objetos y representamos "sección" -> "campos de sección" de acuerdo con este esquema. Por lo tanto, podemos mostrar varios campos para la sección o en cada uno a la vez, si necesita un título y una nota. Cómo funciona el renderizado, lo mostraremos un poco más tarde.
Un breve ejemplo de una configuración:

 export const config: IFieldsFormMetaModel[] = [ { sectionName: ' ', fieldsForm: [{ name: 'subject', label: '', type: ElementTypes.Text, }], }, { sectionName: '', sectionDescription: '  ', fieldsForm: [{ name: 'reminder', disabled: true, label: '', type: ElementTypes.CheckBox, checked: true, }], }, ]; 

En función de los datos comerciales, se establecen los valores para las teclas `name`. Los mismos valores se usan en las teclas prop `fields` para transmitir los valores originales o modificados para el formic.

Para el ejemplo anterior, `fields` podría verse así:

 const fields: SomeBusinessApiFields = { subject: '  ', reminder: 'yes', } 

Para la validación, necesitamos pasar el esquema Yup. Damos el formulario al formulario con los accesorios del contenedor, describiendo sus interacciones con datos externos, por ejemplo, solicitudes.

El formulario no puede influir en el esquema de ninguna manera, por ejemplo:

 export const CreateClientSchema: ( props: CreateClientProps, ) => Yup.MixedSchema = (props: CreateClientProps) => Yup.object( { subject: Yup.string(), description: Yup.string(), date: dateSchema, address: addressSchema(props), }, ); 

Renderizado y optimización de campo


Para renderizar, creamos un mapa para búsqueda rápida por clave. Parece conciso y la búsqueda es más rápida que con `switch`.

 fieldsMap: Record< ElementTypes, ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo, ) => JSX.Element > = { [ElementTypes.Text]: ( state: FormikFieldState, handlers: FormikHandlersState, field: IFieldsFormInfo ) => { const { values, errors, touched } = state; return ( <FormTextField key={field.name} element={field} handleChange={this.handleChangeField(handlers.setFieldValue, field.name)} handleBlur={handlers.handleBlur} value={values[field.name]} error={touched[field.name] && errors[field.name] || ''} /> ); }, [ElementTypes.TextSearch]: (...) => {...}, [ElementTypes.TextArea]: (...) => {...}, [ElementTypes.Date]: (...) => {...}, [ElementTypes.CheckBox]: (...) => {...}, [ElementTypes.RadioButton]: (...) => {...}, [ElementTypes.Select]: (...) => {...}, }; 

Cada campo componente tiene estado. Se encuentra en un archivo separado y está envuelto en `React.memo`. Todos los valores se transmiten a través de accesorios, sin pasar por los 'hijos', para evitar un renderizador innecesario.

Conclusión


Nuestro formulario no es ideal, para cada caso tenemos que crear un contenedor para trabajar con datos. Guárdelos en la `tienda`, convierta y haga solicitudes. Hay una repetición de código del que desea deshacerse. Estamos tratando de encontrar una nueva solución en la que el formulario, dependiendo de los accesorios, tome la clave deseada de la tienda con campos, acciones, diagramas y configuración. En una de las siguientes publicaciones le diremos qué surgió.

Source: https://habr.com/ru/post/461919/


All Articles