use-field

use-field

useField 훅은 단일 입력 필드의 상태(값, 터치 여부, dirty 여부, 오류 등)를 관리하고 입력 요소에 전달할 props를 제공해요. 훅이 반환하는 객체의 각 속성을 하나씩 설명해 드릴게요.

출처: 문서

본문

useField 훅은 다음 객체를 반환해요:

export interface UseFieldReturnType<ValueType> {
  /** Returns props to pass to the input element */
  getInputProps: () => {
    /* props for input component */
  };

  /** Returns current input value */
  getValue: () => ValueType;

  /** Sets the input value to the given value */
  setValue: (value: ValueType) => void;

  /** Resets the field value to initial state, sets touched state to false, sets error to null */
  reset: () => void;

  /** Validates the current input value when called */
  validate: () => Promise<React.ReactNode | void>;

  /** Set to true when the async validate function is called, stays true until the returned promise resolves */
  isValidating: boolean;

  /** Current error message */
  error: React.ReactNode;

  /** Sets the error message to the given react node */
  setError: (error: React.ReactNode) => void;

  /** Returns true if the input has been focused at least once */
  isTouched: () => boolean;

  /** Returns true if input value is different from the initial value */
  isDirty: () => boolean;

  /** Resets the touched state to false */
  resetTouched: () => void;

  /** key that should be added to the input when the mode is uncontrolled */
  key: number;
}

blur 시 검증 (Validate on blur)

blur 시 필드를 검증하려면 validateOnBlur 옵션을 true로 설정해요:

import { TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function Demo() {
  const field = useField({
    initialValue: '',
    validateOnBlur: true,
    validate: (value) => (value.trim().length < 2 ? 'Value is too short' : null),
  });

  return <TextInput {...field.getInputProps()} label="Name" placeholder="Enter your name" />;
}

변경 시 검증 (Validate on change)

변경 시 필드를 검증하려면 validateOnChange 옵션을 true로 설정해요:

import { TextInput } from '@mantine/core';
import { useField, isEmail } from '@mantine/form';

function Demo() {
  const field = useField({
    initialValue: '',
    validateOnChange: true,
    validate: isEmail('Invalid email'),
  });

  return <TextInput {...field.getInputProps()} label="Email" placeholder="Enter your email" />;
}

비동기 검증 (Async validation)

validate 옵션은 비동기/동기 함수 모두 받아요. 두 경우 모두 사용자에게 표시할 오류 메시지나, 값이 유효하면 null을 반환해야 해요. 비동기 검증 상태를 추적하려면 isValidating 속성을 사용해요:

import { Button, Loader, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function validateAsync(value: string): Promise<string | null> {
  return new Promise((resolve) => {
    window.setTimeout(() => {
      resolve(value === 'mantine' ? null : 'Value must be "mantine"');
    }, 800);
  });
}

function Demo() {
  const field = useField({
    initialValue: '',
    validate: validateAsync,
  });

  return (
    <>
      <TextInput
        {...field.getInputProps()}
        label="Enter 'mantine'"
        placeholder="Enter 'mantine'"
        rightSection={field.isValidating ? <Loader size={18} /> : null}
        mb="md"
      />
      <Button onClick={field.validate}>Validate async</Button>
    </>
  );
}

비동기 검증은 validateOnBlur 옵션과 함께 쓸 수 있지만, validateOnChange와는 권장하지 않아요. 키를 누를 때마다 검증이 실행되어 경쟁 조건(race condition)이 생길 수 있기 때문이에요:

import { Loader, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function validateAsync(value: string): Promise<string | null> {
  return new Promise((resolve) => {
    window.setTimeout(() => {
      resolve(value === 'mantine' ? null : 'Value must be "mantine"');
    }, 800);
  });
}

function Demo() {
  const field = useField({
    initialValue: '',
    validateOnBlur: true,
    validate: validateAsync,
  });

  return (
    <TextInput
      {...field.getInputProps()}
      label="Enter 'mantine'"
      placeholder="Enter 'mantine'"
      rightSection={field.isValidating ? <Loader size={18} /> : null}
    />
  );
}

터치와 dirty (Touched and dirty)

필드가 한 번이라도 포커스된 적이 있는지 확인하려면 isTouched 메서드를, 값이 초기값에서 변경되었는지 확인하려면 isDirty 메서드를 사용해요:

import { Text, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function Demo() {
  const field = useField({ initialValue: '' });

  return (
    <>
      <TextInput {...field.getInputProps()} label="Name" placeholder="Enter your name" mb="md" />

      <Text fz="sm">
        Dirty:{' '}
        <Text span inherit c={field.isDirty() ? 'red' : 'teal'}>
          {field.isDirty() ? 'dirty' : 'not dirty'}
        </Text>
      </Text>
      <Text fz="sm">
        Touched:{' '}
        <Text span inherit c={field.isTouched() ? 'red' : 'teal'}>
          {field.isTouched() ? 'touched' : 'not touched'}
        </Text>
      </Text>
    </>
  );
}

변경 시 오류 지우기 (Clear error on change)

기본적으로 값이 변경되면 오류 메시지가 지워져요. 이 동작을 비활성화하려면 clearErrorOnChange 옵션을 false로 설정해요:

import { Button, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function Demo() {
  const field = useField({
    initialValue: '',
    clearErrorOnChange: false,
    validate: (value) => (value.trim().length < 2 ? 'Value is too short' : null),
  });

  return (
    <>
      <TextInput {...field.getInputProps()} label="Name" placeholder="Enter your name" mb="md" />
      <Button onClick={field.validate}>Validate</Button>
    </>
  );
}

비제어 모드 (Uncontrolled mode)

use-field 훅의 비제어 모드는 use-form의 비제어 모드와 비슷하게 동작해요. 비제어 모드에서는 다시 렌더링을 최소화하고 입력 값은 입력 요소 스스로가 관리해요. 제어 모드에서 성능 문제가 있다면 유용하지만, 대부분의 경우 제어 모드가 React state로 항상 최신 필드 정보를 제공하므로 권장돼요.

import { Button, TextInput } from '@mantine/core';
import { useField } from '@mantine/form';

function Demo() {
  const field = useField({
    mode: 'uncontrolled',
    initialValue: '',
    validate: (value) => (value.trim().length < 2 ? 'Value is too short' : null),
  });

  return (
    <>
      <TextInput
        {...field.getInputProps()}
        key={field.key}
        label="Name"
        placeholder="Enter your name"
        mb="md"
      />
      <Button onClick={field.validate}>Validate</Button>
    </>
  );
}

더 알아보기 (Learn more)