use-form

use-form

useForm 훅은 @mantine/form 패키지의 핵심으로, 폼 상태를 관리하고 검증과 제출을 처리해요. 다른 어떤 라이브러리에도 의존하지 않아 @mantine/core 입력 없이도 단독으로 사용할 수 있어요. 주요 API를 하나씩 설명해 드릴게요.

출처: 문서

본문

설치

@mantine/form 패키지는 다른 어떤 라이브러리에도 의존하지 않아요. @mantine/core 입력과 함께 써도 되고, 없이 써도 돼요:

yarn add @mantine/form

사용법

import { Button, Checkbox, Group, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';

function Demo() {
  const form = useForm({
    mode: 'uncontrolled',
    initialValues: {
      email: '',
      termsOfService: false,
    },

    validate: {
      email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
    },
  });

  return (
    <form onSubmit={form.onSubmit((values) => console.log(values))}>
      <TextInput
        withAsterisk
        label="Email"
        placeholder="[email protected]"
        key={form.key('email')}
        {...form.getInputProps('email')}
      />

      <Checkbox
        mt="md"
        label="I agree to sell my privacy"
        key={form.key('termsOfService')}
        {...form.getInputProps('termsOfService', { type: 'checkbox' })}
      />

      <Group justify="flex-end" mt="md">
        <Button type="submit">Submit</Button>
      </Group>
    </form>
  );
}

API 개요

아래 예제들은 모두 다음 예시 useForm 훅을 사용해요:

import { useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    path: '',
    path2: '',
    user: {
      firstName: 'John',
      lastName: 'Doe',
    },
    fruits: [
      { name: 'Banana', available: true },
      { name: 'Orange', available: false },
    ],
    accepted: false,
  },
});

값 (Values)

Form values guide:

// get the current form values
form.getValues();

// Set all form values
form.setValues(values);

// Set all form values using the previous state
form.setValues((prev) => ({ ...prev, ...values }));

// Set the value of a single field
form.setFieldValue('path', value);

// Set the value of a nested field
form.setFieldValue('user.firstName', 'Jane');

// Resets form values to `initialValues`,
// clears all validation errors,
// resets touched and dirty state
form.reset();

// Reset the field at `path` to its initial value
form.resetField('path');

// Sets initial values, used when the form is reset
form.setInitialValues({ values: 'object' });

목록 항목 (List items)

Nested fields guide:

// Inserts the given list item at the specified path
form.insertListItem('fruits', { name: 'Apple', available: true });

// An optional index may be provided to specify the position in a nested field.
// If the index is provided, the item will be inserted at the given position.
// If the index is larger than the current list, the element is inserted at the last position.
form.insertListItem('fruits', { name: 'Orange', available: true }, 1);

// Removes the list item at the specified path and index.
form.removeListItem('fruits', 1);

// Replaces the list item at the specified path and index with the given item.
form.replaceListItem('fruits', 1, { name: 'Apple', available: true });

// Swaps two items of the list at the specified path.
// You should make sure that there are elements at the `from` and `to` index.
form.reorderListItem('fruits', { from: 1, to: 0 });

검증 (Validation)

Form validation guide:

import { useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    email: '',
    user: {
      firstName: '',
      lastName: '',
    },
  },
  validate: {
    email: (value) => (value.length < 2 ? 'Invalid email' : null),
    user: {
      firstName: (value) =>
        value.length < 2
          ? 'First name must have at least 2 letters'
          : null,
    },
  },
});

// Validates all fields with the specified `validate` function or schema, sets form.errors
await form.validate();

// Validates a single field at the specified path, sets form.errors
await form.validateField('user.firstName');

// Works the same way as form.validate but does not set form.errors, returns Promise<boolean>
await form.isValid();
await form.isValid('user.firstName');

// true while any async validation is running
form.validating;

// true while async validation is running for a specific field
form.isValidating('email');

오류 (Errors)

Form errors guide. 검증 오류는 정의된 검증 규칙을 위반했을 때, useForm 속성에 initialErrors를 지정했을 때, 또는 검증 오류를 수동으로 설정했을 때 발생해요:

// get the current errors state
form.errors;

// Set all errors
form.setErrors({ path: 'Error message', path2: 'Another error' });

// Set an error message at the specified path
form.setFieldError('user.lastName', 'No special characters allowed');

// Clears all errors
form.clearErrors();

// Clears the error of the field at the specified path
form.clearFieldError('path');

onReset과 onSubmit

폼 onSubmit과 onReset 이벤트 핸들러를 위한 래퍼 함수예요. onSubmit 핸들러는 두 번째 인자로 검증 실패 시 오류 객체와 함께 호출될 함수를 받아요:

import { useForm } from '@mantine/form';

function Demo() {
  const form = useForm({ mode: 'uncontrolled' });

  const handleSubmit = (values: typeof form.values) => {
    console.log(values);
  };

  return (
    <>
      {/* Supply handle submit as a single argument to receive validated values */}
      <form onSubmit={form.onSubmit(handleSubmit)} />

      {/* Supply a second argument to handle errors */}
      <form
        onSubmit={form.onSubmit(
          (values, event) => {
            console.log(
              values, // <- form.getValues() at the moment of submit
              event // <- form element submit event
            );
          },
          (validationErrors, values, event) => {
            console.log(
              validationErrors, // <- form.errors at the moment of submit
              values, // <- form.getValues() at the moment of submit
              event // <- form element submit event
            );
          }
        )}
      />

      {/* form.onReset calls form.reset */}
      <form onReset={form.onReset}></form>
    </>
  );
}

onSubmitPreventDefault 옵션

기본적으로 폼 onSubmit 핸들러에서 event.preventDefault()가 호출돼요. 이 동작을 바꾸려면 useForm 훅에 onSubmitPreventDefault 옵션을 전달할 수 있어요. 다음 값을 가질 수 있어요:

  • always (기본값) — 항상 event.preventDefault() 호출
  • never — 절대 event.preventDefault() 호출하지 않음
  • validation-failed — 검증이 실패한 경우에만 event.preventDefault() 호출
import { useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  onSubmitPreventDefault: 'never',
});

터치와 dirty (Touched and dirty)

Touched & dirty guide:

// Returns true if the user interacted with any field inside the form in any way
form.isTouched();

// Returns true if the user interacted with the field at the specified path
form.isTouched('path');

// Set all touched values
form.setTouched({ 'user.firstName': true, 'user.lastName': false });

// Clears the touched status of all fields
form.resetTouched();

// Returns true if form values are not deep equal to initialValues
form.isDirty();

// Returns true if the field value is not deep equal to initialValues
form.isDirty('path');

// Sets the dirty status of all fields
form.setDirty({ 'user.firstName': true, 'user.lastName': false });

// Clears the dirty status of all fields, saves form.values snapshot
// After form.resetDirty is called, form.isDirty will compare
// form.getValues() to the snapshot instead of initialValues
form.resetDirty();

UseFormReturnType

UseFormReturnType는 form을 다른 컴포넌트에 prop으로 전달할 때 사용할 수 있어요:

import { TextInput } from '@mantine/core';
import { useForm, UseFormReturnType } from '@mantine/form';

interface FormValues {
  name: string;
  occupation: string;
}

function NameInput({
  form,
}: {
  form: UseFormReturnType<FormValues>;
}) {
  return (
    <TextInput
      key={form.key('name')}
      {...form.getInputProps('name')}
    />
  );
}

function OccupationInput({
  form,
}: {
  form: UseFormReturnType<FormValues>;
}) {
  return (
    <TextInput
      key={form.key('occupation')}
      {...form.getInputProps('occupation')}
    />
  );
}

function Demo() {
  const form = useForm<FormValues>({
    mode: 'uncontrolled',
    initialValues: { name: '', occupation: '' },
  });
  return (
    <>
      <NameInput form={form} />
      <OccupationInput form={form} />
    </>
  );
}

더 알아보기 (Learn more)