Настраивать

Formik - прекрасная библиотека для ускорения процесса создания форм в React. Он обрабатывает все основные функции, такие как состояние формы, проверка и отправка.

Начнем с создания пустого create-response-app https://create-react-app.dev/

npx create-react-app my-app

Установите библиотеку formik

yarn add formik

Использование компонентов Formik

Удалите созданный пример кода и импортируйте библиотеку formik и некоторые компоненты в App.js. Мы будем использовать реакцию

useState

hook только для того, чтобы показать значения формы в этом примере.

import React, { useState } from 'react'
import { Formik, Form, Field } from 'formik'
function App() {
  const [result, setResult] = useState('')
  return (
    // Formik is the main component that handles all the logic
    // Form is just a regular html <form> wrapper
    <Formik>
      {() => (
        <Form>
        </Form>
      )}
      </Formik>
  );
}
export default App

Давайте использовать

Field

Компонент formik для создания некоторых полей. Проверьте документацию, чтобы увидеть все возможные доступные реквизиты https://jaredpalmer.com/formik/docs/api/field#props-1

Поле по умолчанию - это input type = ”text”

<Field name="fieldName" />

Для выбора мы указываем параметры как дочерние

<Field as="select" name="color" value="none">
    <option value="none">Pick a color</option>
    <option value="red">Red</option>
    <option value="green">Green</option>
    <option value="blue">Blue</option>
  </Field>

В этом примере мы собираемся использовать текстовое поле для отображения значений после отправки.

<Field as="textarea" value={result} rows={5} />

Свойства формы передаются детям, предоставляя множество вариантов для настраиваемых полей.

<Field name="email">
    {({ field }) => (
      <div>
        <label>email:</label>
        <input type="email" required placeholder="Email" {...field} />
      </div>
    )}
  </Field>

Мы можем пойти дальше и просто установить компонентную опору. Реквизиты формы будут переданы этому компоненту

function CustomInput({ field, form, ...props }) {
  return <input {...field} {...props} />
}

<Field
  name="name"
  required
  placeholder="Name"
  component={CustomInput}
/>

A

type="submit"

автоматически обрабатывает форму onSubmit

<button type="submit">Submit</button>

Собери все вместе

Это последний пример формы, созданной с помощью formik. Formik нуждается в

initialValues

опора для правильной работы.

import React, { useState } from 'react'
import { Formik, Form, Field } from 'formik'
function CustomInput({ field, form, ...props }) {
  return <input {...field} {...props} />
}
function App() {
  const [result, setResult] = useState('')
  return (
    <Formik
      initialValues={{
        email: '',
        name: '',
        color: 'red'
      }}
      onSubmit={(values, actions) => {
        setResult(JSON.stringify(values))
      }}
    >
      {() => (
        <Form>
          <Field as="select" name="color" value="none">
            <option value="none">Pick a color</option>
            <option value="red">Red</option>
            <option value="green">Green</option>
            <option value="blue">Blue</option>
          </Field>
          <br />
          <Field name="email">
            {({ field }) => (
              <div>
                <label>email:</label>
                <input
                  type="email"
                  required
                  placeholder="Email"
                  {...field}
                />
              </div>
            )}
          </Field>
          <br />
          <Field
            name="name"
            required
            placeholder="Name"
            component={CustomInput}
          />
          <br />
          <button type="submit">Submit</button>
          <br />
          <br />
          <Field
            style={{ width: '100%'}}
            as="textarea"
            value={result}
            rows={2}
          />
        </Form>
      )}
      </Formik>
  )
}
export default App

Не так уж и красиво, но я не хотел загрязнять код стилями.

Модульное тестирование с помощью библиотеки тестирования React

Обычно для тестирования компонентов достаточно использовать Jest и Enzyme, но с Formik, внутренним состоянием React и события становятся более сложными, а

input.simulate('change')

не работает. К счастью, есть хорошая программа под названием Testing Library, которая поддерживает множество интерфейсных библиотек и фреймворков https://testing-library.com/docs/intro

Прежде всего, установите библиотеку как зависимость разработчика

yarn add --dev '@testing-library/react'

Сначала проверьте, не дает ли приложение сбой

it('renders without crashing', () => {
  const div = document.createElement('div')
  ReactDOM.render(<App />, div)
})

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

it('submits correct values', () => {
  const { container } = render(<App />)
  const name = container.querySelector('input[name="name"]')
  const email = container.querySelector('input[name="email"]')
  const color = container.querySelector('input[name="color"]')
  const submit = container.querySelector('button[type="submit"]')
})

Давай изменим их ценности

fireEvent.change(name, {
    target: {
      value: 'mockname'
    }
  })
  fireEvent.change(email, {
    target: {
      value: 'mockemail'
    }
  })
  fireEvent.change(color, {
    target: {
      value: 'mockcolor'
    }
  })

Получаем эту ошибку

Warning: An update to Formik inside a test was not wrapped in act(...).
    
    When testing, code that causes React state updates should be wrapped into act(...):
    
    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */

Это означает, что что-то изменилось в Formik state внутри теста, нам нужно дождаться этих изменений. act тоже хорошо, но вместо act мы также можем использовать библиотеку тестирования

wait

Https://testing-library.com/docs/dom-testing-library/api-async#wait

Обратите внимание, что мы изменим тест на

async
it("submits correct values", async () => {
  const { container } = render(<App />)
  const name = container.querySelector('input[name="name"]')
  const email = container.querySelector('input[name="email"]')
  const color = container.querySelector('select[name="color"]')
  const submit = container.querySelector('button[type="submit"]')
  const results = container.querySelector("textarea");
  await wait(() => {
    fireEvent.change(name, {
      target: {
        value: "mockname"
      }
    })
  })
  await wait(() => {
    fireEvent.change(email, {
      target: {
        value: "[email protected]"
      }
    })
  })
  await wait(() => {
    fireEvent.change(color, {
      target: {
        value: "green"
      }
    })
  })
  await wait(() => {
    fireEvent.click(submit)
  })
  expect(results.innerHTML).toBe(
    '{"email":"[email protected]","name":"mockname","color":"green"}'
  )
})

Теперь мы получаем правильные результаты, и благодаря библиотеке тестирования мы фокусируемся на функциональности и поведении DOM, а не на деталях React.

Вы можете пойти дальше и прочитать документацию Formik для создания сложных форм и полей. Спасибо за прочтение.

ОБНОВЛЕНИЕ: библиотека тестирования "wait" устарела. Вместо этого используйте "waitFor".