Использование преобразователей данных между Forms и API

«В предыдущей статье я подробно описал, как обрабатывать большие формы Formik, используя подформы и компонуя их в более крупной форме. В типичных корпоративных приложениях цель большой формы — зафиксировать пользовательский ввод, который будет отправлен на сервер через REST или GraphQL. Самая большая проблема, с которой мы сталкиваемся, — это работа с вложенными данными API, которые плохо вписываются в плоскую форму. Типичный вариант использования выглядит следующим образом:

  • API возвращает бизнес-объект и наполняет им форму.
  • Пользователь редактирует это и отправляет значения.
  • Действие отправки формы выполняет вызов API прямо или косвенно, используя отредактированные значения.

Проблема заключается в том, чтобы сопоставить значения JSON Formik с полезной нагрузкой API и наоборот. Дизайн форм UX может не соответствовать API напрямую. Проблема усугубляется тем, что API всегда не обязательно дружественны к пользовательскому интерфейсу. Некоторые из них предназначены для общественного потребления и не адаптированы для взаимодействия с пользователем. Это негативно влияет на опыт разработчиков. Вот стратегия, которая может изящно решить эту проблему:

Значения формы и формы объекта полезной нагрузки API не обязательно должны совпадать на 100 %.

Что нам нужно, так это преобразования данных между этими двумя сущностями.

toFormValues() и toApiPayload() — это простые, чистые функции преобразования данных. Для них очень легко писать модульные тесты.

Теперь ваша форма свободна от ограничений данных API и может соответствовать дизайну UX и полям формы библиотеки компонентов. Например, данные, которые появляются в раскрывающемся списке «Выбрать», не нужно крутить руками, чтобы они соответствовали требованиям формы.

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

Интерфейсы TypeScript необходимы, когда вы имеете дело со сложными формами данных. Без него слишком велика вероятность человеческих ошибок. Я очень рекомендую это. По сути, ваши типы данных и функции будут выглядеть так:

interface IApiValues {
  user: {
    id: string;
    fullName: string; 
  }
} 
interface IFormValues {
  userId: string;
  userName: string; 
}
type toFormValuesType = (apiData: IApiValues) => IFormValues;
type toApiPayloadType = (values: IFormValues) => IApiValues;

Реализуйте свои трансформеры:

const toFormValues: toFormValuesType = apiData => ({
  userId: apiData.user.id,
  userName: apiData.user.fullName
});
const toApiPayload: toApiPayloadType = values => ({
  user: {
    id: values.userId,
    fullName: values.userName
  }
});

Теперь вставьте их в форму Formik:

const submitForm = (values: IFormValues) => { 
  submitToApi(toApiPayload(values));
}
<Formik
  initialValues={toFormValues(apiData)}
  onSubmit={submitForm}
  children={YourFormContainer}
/>

Решение довольно простое. Тем не менее, он сохраняет ваши формы чистыми, делегируя преобразование вспомогательным функциям. Они могут оставаться верными дизайну UX и доступным компонентам ввода. Вам не нужно захламлять код формы спорящими данными повсюду. Кроме того, ваши преобразования данных становятся очень удобными для модульного тестирования.

describe("form/api data transformations", () => {
  const apiData: IApiValues = {
    user: {
      id: "1234",
      fullName: "Naroth"
    }
  };
  const formData: IFormValues = {
    userId: "1234",
    userName: "Naroth"
  };
  it("should transform correctly", () => {
    expect(toFormValues(apiData)).toEqual(formData);
    expect(toApiPayload(formData)).toEqual(apiData);
  });
// Alternatively you could do it in one shot
// But it is better to test them individually
  it("should transform back and forth correctly", () => {
    expect(toApiPayload(toFormValues(apiData))).toEqual(apiData);
  });
// You can also use snapshot testing to keep a reference
// of the object. It helps to understand the logic and aids
// debugging.
  it("toFormValues works", () => {
    expect(toFormValues(apiData)).toMatchSnapshot();
  });
});

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