Reutilizando formulários no React

Oi

Temos um painel de administração e muitos formulários no BCS, mas na comunidade React não existe um método geralmente aceito - como projetá-los para reutilização. O guia oficial do Facebook não possui informações detalhadas sobre como trabalhar com formulários em condições reais, onde a validação e a reutilização são necessárias. Alguém usa redux-form, formik, final-form, ou mesmo escreve sua própria solução.


Neste artigo, mostraremos uma das opções para trabalhar com formulários no React. Nossa pilha será assim: React + formik + Typescript. Vamos mostrar:

  • O que um componente deve fazer.
  • Configuração, campos e validação no nível de adereços.
  • Como tornar um formulário reutilizável.
  • Otimização do renderizador.
  • Do que o nosso método é inconveniente.

Com a nova tarefa de negócios, aprendemos que precisaremos criar 15 a 20 formas semelhantes e, hipoteticamente, pode haver ainda mais. Tínhamos uma forma de dinossauro na configuração, que trabalhava com dados da `store`, enviava ações para salvar e executar solicitações através de` sagas`. Ela era maravilhosa, valorizando os negócios. Mas já não era expansível nem reutilizável, apenas com código ruim e adição de muletas.

A tarefa é: reescrever o formulário para que possa ser reutilizado um número ilimitado de vezes. Bem, lembre-se da programação funcional, ela possui funções puras que não usam dados externos, no nosso caso `redux`, apenas o que eles são enviados em argumentos (props).

E foi o que aconteceu.

A idéia do nosso componente é que você crie um invólucro (contêiner) e escreva nele a lógica de trabalhar com o mundo exterior (recebendo dados da loja Redux e enviando ações). Para isso, o componente do contêiner deve poder receber algumas informações através dos retornos de chamada. A lista inteira de adereços de formulário:

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 o 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} /> ); } 

No prop'e do formulário `validate`, chamamos o método` this.validateFormLevel`, no qual damos ao componente container a oportunidade de obter todos os campos alterados e verificar se são 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 }); }); } } 

Aqui temos que chamar a validação novamente para deixar claro para o contêiner se os campos são válidos. Ao enviar um formulário, simplesmente chamamos prop `onSubmit`:

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

Com os adereços 1-5, tudo deve ficar claro. Vamos seguir para 'config', 'fields' e 'validationSchema'.

Adereços '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   }>; } 

Com base nessa interface, criamos uma matriz de objetos e renderizamos “section” -> “section fields” de acordo com este esquema. Assim, podemos mostrar vários campos para a seção ou em cada um de cada vez, se você precisar de um título e uma nota. Como a renderização funciona, mostraremos um pouco mais tarde.
Um pequeno exemplo de uma configuração:

 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, }], }, ]; 

Com base nos dados comerciais, os valores para as chaves `name` são definidos. Os mesmos valores são usados ​​nas chaves prop `fields` para transmitir os valores originais ou alterados para o formic.

Para o exemplo acima, `fields` pode ser assim:

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

Para validação, precisamos passar no esquema Yup. Fornecemos o formulário ao formulário com os adereços de contêiner, descrevendo as interações com dados externos, por exemplo, solicitações.

O formulário não pode influenciar o esquema de forma alguma, por exemplo:

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

Otimização de renderização e campo


Para renderização, fizemos um mapa para pesquisa rápida por chave. Parece conciso e a pesquisa é mais rápida do que por `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 do componente é com estado. Ele está localizado em um arquivo separado e está envolvido em `React.memo`. Todos os valores são transmitidos através de adereços, ignorando os `filhos ', para evitar renderizadores desnecessários.

Conclusão


Nosso formulário não é ideal; para cada caso, precisamos criar um invólucro de contêiner para trabalhar com dados. Salve-os na `loja`, converta e faça solicitações. Há uma repetição de código da qual você deseja se livrar. Estamos tentando encontrar uma nova solução na qual o formulário, dependendo dos objetos, terá a chave desejada da loja com campos, ações, diagramas e configurações. Em uma das postagens a seguir, contaremos o que aconteceu.

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


All Articles