본문 바로가기

React

React가 추천하는 form 상태관리 툴 formik

 

 

  formik은 리액트 공식문서에서 추천하는 form의 상태관리 툴이다. 폼 엘리먼트는 엘리먼트 자체가 내부 상태를 가지는데, 그래서 다른 DOM 엘리먼트와 다르게 동작한다. 그러다 보니 input, textarea, select와 같은 폼 엘리먼트는 리액트에서는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다.

  폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어하는데 이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 제어 컴포넌트 (controlled component)라고 한다. 이런 제어 컴포넌트들을 한 앱에서 한 번만 사용하는 것이 아니라 여러 번 또는 아주 많이 사용할 확률이 높다. 그러다 보니 각 제어 컴포넌트마다 state 변화를 직접 처리해줘야 하기 때문에 작업량도 많고 코드가독성도 당연히 떨어진다. 이것을 보완해 주는 라이브러리가 formik이다. formik은 3가지 부분을 도와준다. form state 값 가져오기, validation을 에러 메시지 출력, form submit 핸들링.

  여러 방법들이 있지만 내가 생각하기 가장 편한 방법으로 formik을 사용하고자 한다. 일단 나의 경우 css in js를 주로 사용하기 때문에 내장 컴포넌트는 지양한다. 그리고 hook을 사용하여 어느 순간과 시점에서도 편하게 사용하길 원한다. 그래서 useFormik hook을 사용한다. useFormik을 사용할 때는 initialValue와 onSubmit 함수를 전달해야 한다. initialValue는 폼 엘리먼트의 초기 value이고, onSubmit은 폼 엘리먼트 제출 시 실행되는 콜백 함수이다. 

import { useFormik, FormikValues } from 'formik'

 const SignUpForm: React.FC = () => {
	const formik = useFormik({
		initialValues: {
			email: '',
			firstName: '',
			lastName: ''
		},
		onSubmit: (values: FormikValues) => {
			console.log(values)
		}
	{)
});

 

  여기에 validation 기능 역시 제공한다. validation은 useFormik에 validate property에 객체를 직접 정의하거나 yup을 사용하는 방법이 있다. 나는 yup을 사용하여 validation을 사용해 보았다.

import {.useFormik, FormikValues } from 'formik'
import * as yup from 'yup'

 const SignUpForm: React.FC = () => {
	const formik = useFormik({
		initialValues: {
			email: '',
			firstName: '',
			lastName: ''
		},
		onSubmit: (values: FormikValues) => {
			console.log(values)
		},
		validationschema: yup.object().shape({
			email: yup.string().email('Invalid email address').required('Required'),
			firstName: yup.string().max(15, 'Must be 15 characters or less').required('Required'),
			lastName: yup.string().max(20, 'Must be 20 characters or less').required('Required')
		})
	})
});

이렇게 사용한 후 반환되는 JSX는 이렇게 한다.

return (
	<form onSubmit={formik.handleSubmit}>
		<label htmlFor='firstName'>First Name</label>
		<input
			id='firstName'
			name='firstName'
			type='text'
			onChange={formik.handleChange}
			value={formik.values.firstName}
		/>
		<label htmlFor='lastName'>Last Name</label>
		<input
			id='lastName'
			name='lastName'
			type='text'
			onChange={formik.handleChange}
			value={formik.values.lastName}
		/>
		<label htmlFor='email'>Email Address</label>
		<input
			id='email'
			name='email'
			type='text'
			onChange={formik.handleChange}
			value={formik.values. email}
		/>

		<button type='submit'>Submit</button>
	</form>
)

이렇게 firstName, lastName, email 3가지 필드를 입력받는 form이지만 formik 객체로 3개의 input을 모두 관리하는 것을 볼 수 있다. 그러나 여기에도 onChangedhk value 같이 중복적으로 사용되는 것들이 있다. 이것들은 getFieldProps 메소드를 통해 해결할 수 있다.

return (
	<form onSubmit={formik.handleSubmit}>
		<label htmlFor='firstName'>First Name</label>
		<input
			id='firstName'
			type='text'
			{...formik.getFieldProps('firstName')}
		/>
		<label htmlFor='lastName'>Last Name</label>
		<input
			id='lastName'
			type='text'	
			{...formik.getFieldProps('lastName')}
		/>
		<label htmlFor='email'>Email Address</label>
		<input
			id='email'
			type='text'
			{...formik.getFieldProps('email')}
		/>

		<button type='submit'>Submit</button>
	</form>
)

 

  최종적으로 코드를 완성해 보면 이렇다.

import {.useFormik, FormikValues } from 'formik'
import * as yup from 'yup'

const SignUpForm: React.FC = () => {
	const formik = useFormik({
		initialValues: {
			email: '',
			firstName: '',
			lastName: ''
		},
		onSubmit: (values: FormikValues) => {
			console.log(values)
		},
		validationschema: yup.object().shape({
			email: yup.string().email('Invalid email address').required('Required'),
			firstName: yup.string().max(15, 'Must be 15 characters or less').required('Required'),
			lastName: yup.string().max(20, 'Must be 20 characters or less').required('Required')
		})
	})
	
	return (
	<form onSubmit={formik.handleSubmit}>
		<label htmlFor='firstName'>First Name</label>
		<input
			id='firstName'
			type='text'
			{...formik.getFieldProps('firstName')}
		/>
		{formik.touched.firstName && formik.errors.firstName ? (
			<div>{formik.errors.firstName}</div>
		) : null}
			
		<label htmlFor='lastName'>Last Name</label>
		<input
			id='lastName'
			type='text'	
			{...formik.getFieldProps('lastName')}
		/>
		{formik.touched.lastName && formik.errors.lastName ? (
			<div>{formik.errors.lastName}</div>
		) : null}

		<label htmlFor='email'>Email Address</label>
		<input
			id='email'
			type='text'
			{...formik.getFieldProps('email')}
		/>
		{formik.touched.email && formik.errors.email ? (
			<div>{formik.errors.email}</div>
		) : null}

		<button type='submit'>Submit</button>
	</form>
	)
};

'React' 카테고리의 다른 글

Context  (0) 2023.03.16
코드 분할 (Code Splitting)  (0) 2023.03.14
Yup으로 유효성 검사하기  (0) 2023.03.14
useEffect의 mount와 unmount  (0) 2023.03.09
잘못된 useState 사용 줄이기  (0) 2023.03.08