Formik и Material-UI

Я пытаюсь использовать Formik с текстовым полем Material-UI. Вот так:

import TextField from '@material-ui/core/TextField';
import {
  Field,
  FieldProps,
  Form,
  Formik,
  FormikErrors,
  FormikProps
} from 'formik';
import React, { Component } from 'react';

interface IMyFormValues {
  firstName: string;
}

class CreateAgreementForm extends Component<{}> {
  public render() {
    return (
      <div>
        <h1>My Example</h1>
        <Formik
          initialValues={{ firstName: '' }}
          // tslint:disable-next-line:jsx-no-lambda
          onSubmit={(values: IMyFormValues) => alert(JSON.stringify(values))}
          // tslint:disable-next-line:jsx-no-lambda
          validate={(values: IMyFormValues) => {
            const errors: FormikErrors<IMyFormValues> = {};
            if (!values.firstName) {
              errors.firstName = 'Required';
            }
            return errors;
          }}
          // tslint:disable-next-line:jsx-no-lambda
          render={(formikBag: FormikProps<IMyFormValues>) => (
            <Form>
              <Field
                name="firstName"
                render={({ field, form }: FieldProps<IMyFormValues>) => (
                  <TextField
                    error={Boolean(
                      form.errors.firstName && form.touched.firstName
                    )}
                    helperText={
                      form.errors.firstName &&
                      form.touched.firstName &&
                      String(form.errors.firstName)
                    }
                  />
                )}
              />
            </Form>
          )}
        />
      </div>
    );
  }
}

export default CreateAgreementForm;

Я хочу, чтобы Formik отвечал за валидацию и Material-UI за внешний вид. Я хочу передать error.firstName компоненту TextField, но ошибка не отображается правильно. Как я могу это исправить, чтобы было понятно? Я не хочу писать свой собственный компонент TextField.

Ответ 1

Как упомянуто в комментариях, на самом деле может быть хорошей идеей реализовать компоненты-оболочки, как они это делали в следующих примерах из Formik или ReactFinalForm:

Идея та же: реализовать пользовательские компоненты-обертки, чтобы обернуть компоненты Material-UI и отобразить реквизиты API Formik или ReactFinalForm.

Преимущества этого подхода состоят в том, чтобы централизовать в одном месте отображение между двумя платформами, чтобы вы не повторяли отображение каждый раз, и если одна из структур вносит критические изменения, вам просто нужно изменить эти пользовательские компоненты-оболочки.

Ответ 2

Я не думаю, что вам нужна другая библиотека или даже создать свою собственную оболочку, я думаю, вам нужно немного подправить свой код.

Одна из проблем заключается в том, что вы не передаете функцию onChange в Material TextField, поэтому значение формы firstName всегда равно null, и поэтому вы всегда получаете ошибку, даже если вы ввели имя. Попробуйте добавить имя или идентификатор в свой TextField и функцию onChange следующим образом:

<Field
    validateOnBlur
    validateOnChange
    name="firstName"
    render={({ field, form }) => (
    <TextField
        name={"firstName"}
        error={
            Boolean(form.errors.firstName && form.touched.firstName)
        }
        onChange={formikBag.handleChange}
        onBlur={formikBag.handleBlur}
        helperText={
            form.errors.firstName &&
            form.touched.firstName &&
            String(form.errors.firstName)
        }
    />
    )}
/>

Ответ 3

Вы можете попробовать это: https://github.com/daixianceng/formik-material-fields

Установка:

npm install --save formik-material-fields

Использование:

import React, { Component } from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import { FormikTextField } from 'formik-material-fields';

const validationSchema = Yup.object().shape({
  username: Yup.string().required(),
});

const initialValues = {
  username: '',
};

class MyForm extends Component {
  render() {
    return (
      <Formik
        initialValues={initialValues}
        validationSchema={validationSchema}
        onSubmit={this.props.onSubmit}
      >
        {({ isValid }) => (
          <Form autoComplete="off">
            <FormikTextField
              name="username"
              label="Username"
              margin="normal"
              fullWidth
            />
          </Form>
        )}
      </Formik>
    );
  }
}

Ответ 4

Вы также можете попробовать эту библиотеку, которая выполняет тяжелую работу за вас и реализует код оболочки вокруг компонентов Material-UI (включая <TextField />): https://github.com/stackworx/formik-material-ui.

Установка:

yarn add formik-material-ui

В своем компоненте формы Formik передайте компонент <TextField /> в качестве пропеллера компонента компонента Formik <Field />.

import { Formik, Field, Form } from 'formik';
import { TextField } from 'formik-material-ui';

<Field
  name="email"
  label="Email"
  type="email"
  component={TextField}
/>

Formik продолжит обрабатывать проверку должным образом и отобразит компонент Material UI и сообщение об ошибке. В документации есть дополнительные детали для других компонентов ввода Mui и для помощи в настройке.