antd-mobile Form 상태 다루기

yisu.kim·2022년 6월 30일
0
post-thumbnail

Ant Design Mobile

Explore the limits of mobile web experience

by Ant Design Mobile

회사에서 신규 프로젝트에 antd-mobile을 사용하기로 했다. 페이지 소개에 적힌 문구를 보면 자신감이 넘치는데 광범위하게 제공하는 컴포넌트를 보면 그럴만하다는 생각이 든다. 다만 아직 사용해본 지 며칠 지나지 않았으므로 앞으로 여러 가지 문제를 맞닥뜨리게 될지도 모르겠다.

이 글에서는 입력 폼을 구현하던 도중 부딪힌 어려움에 대해 다룬다. gist 하나 저장해두면 끝인 간단한 문제지만 혹시 같은 고통을 겪는 분들에게 도움이 되기를 바라며 블로그로 공유해본다.

Form

High-performance form controls with built-in data field management. Including data entry, verification and corresponding styles.

by antd-mobile Form

antd-mobile에서는 자체적으로 Form의 필드를 관리하는 유용한 기능을 제공한다. Form.useForm()으로 생성한 FormInstance 객체를 받아 컨트롤을 돕는 form props가 있고 그 밖에도 onFieldsChange, onValuesChange, onFinish, onFinishFailed props 등을 사용해 다양한 이벤트를 관리할 수 있게 도와준다.

React Hook Form

그러나 antd-mobile과 react-hook-form의 상성이 좋은지는 의문이다. 회사의 다른 서비스에서는 모두 이 라이브러리를 사용하고 있었고 mui와 잘 어울리므로 antd에도 쉽게 적용할 수 있을 거라 생각했다. 그런 생각에 가벼운 마음으로 도전했으나 value와 onChange 이벤트 핸들링을 거쳐 에러 메시지를 표시하는 부분에서 난관에 빠졌다.

이리저리 구글링해보고 깃허브 이슈에서 검색해보아도 이전 버전에 대한 해결 방법만 찾을 수 있었다. 약 반나절을 소모한 뒤 antd-mobile의 버전을 내리기보다는 react-hook-form을 배제하고 antd-mobile만 순수하게 사용하기로 했다.

form state를 관리해보자

📦 codesandbox에서 실습이 가능합니다.

문서를 참고해 Form.item의 rules로 에러 메시지를 쉽게 표현할 수 있었다. 그런데 react-hook-form처럼 버튼을 비활성화하려고 하니 isDirty나 isValid와 같은 form state를 따로 제공하지 않는 게 아닌가! 다행히 직접 상태를 관리함으로써 해결할 수 있어 실습 코드와 설명을 남긴다.

Form validation

일단 validation을 추가한 간단한 Form을 살펴보자. rules에 필드가 required여야 한다는 간단한 조건을 추가했다.

export default function SampleForm() {
  return (
    <Form
      footer={
        <Button block type="submit" color="primary">
          Submit
        </Button>
      }
    >
      <Form.Item
        name="name"
        label="Name"
        rules={[{ required: true, message: "Please input your name" }]}
      >
        <Input placeholder="Enter your name" />
      </Form.Item>
    </Form>
  );
}

isDirty & isValid

먼저 form을 컨트롤하기 위해 Form.useForm()으로 FormInstance를 생성한다.
이어서 isDirty와 isValid를 각각 state로 선언하고 둘 중 하나라도 false라면 버튼이 비활성화되도록 조건을 추가한다.
Form의 onFieldsChange 이벤트가 발생할 때 form.isFieldsTouched()에 따라 isDirty를, form.getFieldsError()의 에러 개수에 따라 isValid를 업데이트하면 된다.

export default function SampleForm() {
  const [form] = Form.useForm();
  const [isDirty, setIsDirty] = useState(false);
  const [isValid, setIsValid] = useState(false);

  return (
    <Form
      form={form}
      footer={
        <Button
          block
          type="submit"
          color="primary"
          disabled={!isDirty || !isValid} // isDirty와 isValid를 사용해 값이 입력되지 않았거나 입력된 값이 유효하지 않으면 버튼을 비활성화
        >
          Submit
        </Button>
      }
      onFieldsChange={() => {
        setIsDirty(form.isFieldsTouched()); // 필드가 하나라도 터치(작동)되었다면 form에 변화가 있다고 판단
        const hasError = form
          .getFieldsError()
          .some(({ errors }) => errors.length); // 필드별 에러 메시지가 하나라도 존재하는지 확인
        setIsValid(!hasError); // 에러 메시지가 하나도 없으면 form이 유효하다고 판단
      }}
    >
      <Form.Item
        name="name"
        label="Name"
        rules={[{ required: true, message: "Please input your name" }]}
      >
        <Input placeholder="Enter your name" />
      </Form.Item>
    </Form>
  );
}

아래 이미지처럼 에러 메시지와 버튼 활성화 상태가 자연스럽게 변경되는 것을 확인할 수 있다.

참고자료

잘못된 부분에 대한 지적은 언제든 환영합니다! 😉

0개의 댓글