React hook Form이란 form의 validation을 도와주는 라이브러리이다.
기본적으로 uncontrolled component를 베이스로 지원하고, controlled component에 대한 지원도 하고있다.
ref를 기반으로 하기 때문에 다른 UI라이브러리와 호환이 잘 된다.
useForm
form을 만들기 위해서는 useForm을 사용해야한다.
useForm에는 form을 만들기 위한 다양한 옵션이 있는데, 아래와 같다.
const { register } = useForm({
mode: 'onSubmit',
reValidateMode: 'onChange',
defaultValues: {},
resolver: undefined,
context: undefined,
criteriaMode: "firstError",
shouldFocusError: true,
shouldUnregister: false,
shouldUseNativeValidation: false,
delayError: undefined
})
유효성 트리거가 일어나는 시점을 지정해주는 등 여러가지 옵션이 있는데, 자세한 내용은 공식문서를 확인하자
https://react-hook-form.com/api/useform
기본적으로 form의 초기값을 설정해주는 defaultValues와 유효성 검사를 지정해줄 수 있는 resolver를 자주 사용한다.
useForm을 이용하여 form을 만드는 것을 도와주는 여러함수가 있다.
const {
register
unregister
formState
watch
handleSubmit
reset
resetField
setError
clearErrors
setValue
setFocus
getValues
getFieldState
trigger
control
} = useForm();
register
(name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref })
이 함수에서는 입력/선택을 등록하고, 유효성 검사를 적용할 수 있다.
유효성 검사 규칙은 모두 HTML 표준을 기반으로 하며, 커스텀 유효성 검사 규칙을 적용할 수 있다.
name은 필수이며 단일값(unique)이어야 한다. name은 점과 괄호 구문도 지원하므로, 중첩된 폼 필드를 쉽게 만들 수 있다.
// input 이름 결과
name="firstName" { firstName: 'value'}
name="name.firstName" { name: { firstName: 'value' } }
name="name.firstName[0]" { name: { firstName: [ 'value' ] } }
custom register
커스텀 컴포넌트와 Ref에 접근할 수 없는 경우 입력을 수동으로 등록할 수 있다. 또한 이 작업을 대신 처리해주는 Controller 컴포넌트를 제공하고 있다.
custom register를 사용하면, 입력은 더이상 ref로 등록되지 않으므로, setValue를 통해 입력 값을 업데이트 해주어야 한다.
값을 업데이트하는 동안 커스톰 등록된 입력을 다시 렌더리아 하도록 하려면, 등록된 입력의 타입을 지정해 주어야 한다.
register('firstName', { required: true, min: 8 });
<TextInput onTextChange={(value) => setValue('lastChange', value))} />
example
// not working, because ref is not assigned
<TextInput {...register('test')} />
const firstName = register('firstName', { required: true })
<TextInput
onChange={firstName.onChange}
onBlur={firstName.onBlur}
inputRef={firstName.ref} // you can achieve the same for different ref name such as innerRef
/>
// correct way to forward input's ref
const Select = React.forwardRef(({ onChange, onBlur, name, label }, ref) => (
<select name={name} ref={ref} onChange={onChange} onBlur={onBlur}>
<option value="20">20</option>
<option value="30">30</option>
</select>
));
formState
object
폼 상태에 대한 정보를 포함한다.
watch
(names?: string | string[] | (data, options) => void) => unknown
지정된 인풋을 관찰하고 그 값들을 반환한다.
렌더링 할 대상을 결정할 때 유용하다.
handleSubmit
((data: Object, e?: Event) => void, (errors: Object, e?: Event) => void) => Function
유효성 감사가 완료되었을 때 폼 데이터를 전달한다.
reset
(values?: Record<string, any>, omitResetState?: Record<string, boolean>) => void
폼 안의 필드 값과 에러를 초기화 한다.
setError
(name: string, error: { type?: string, types?: object, message?: string, shouldFocus?: boolean }) => void
한 개 또는 그 이상의 입력값 에러를 수동으로 설정할 수 있다.
인풋과 관련된 에러인 경우 유효성 검사를 통과하면 에러 값을 유지하지 않는다
handleSubmit 안에서 비동기적으로 유효성 검사를 수행한 뒤 사용자에게 에러 피드백을 제공할 때 유용하다
clearErrors
(name?: string | string[]) => void
- undefined: 모든 에러를 리셋
- string : 하나의 에러를 리셋
- string[] : 여러개의 에러 리셋
setValue
(name: string, value: any, config?: Object) => void
동적으로 input/select 값을 설정할 수 있다. 그와 동시에, 아래의 조건이 충족할 때만 다시 렌더링 되어 불피료한 리렌더링을 피한다.
- 값이 업데이트 되면서 에러를 일으킬 때
- 값이 업데이트 되면서 에러를 바로잡을 때
- 맨 처음 실행되어 폼의 isDirty 상태가 true가 되었을 때
example
import * as React from "react";
import * as ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
type FormInputs = {
firstName: string;
lastName: string;
};
const App = () => {
const { register, handleSubmit, errors, setValue, formState } = useForm<
FormInputs
>();
const onSubmit = (data: FormInputs) => {
alert(JSON.stringify(data));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input name="firstName" type="text" ref={register} />
<label>Last Name</label>
<input name="lastName" type="text" ref={register({ minLength: 10 })} />
{errors.lastName && <p>"This Field must have more than 10 characters"</p>}
<button type="button" onClick={() => setValue("firstName", "Grace")}>
Set First Name Value
</button>
<button
type="button"
onClick={() =>
setValue("lastName", "Hopper", {
shouldValidate: true,
shouldDirty: true
})
}
>
Set Last Name
</button>
<input type="submit" />
<label>FormState:</label>
<label>{JSON.stringify(formState)}</label>
</form>
);
};
getValues
(payload?: string | string[]) => Object
폼의 값을 읽을 때 사용한다. watch와 다르게 getValues는 리랜더링을 일으키거나 입력값의 변화를 구독하지 않는다는 것이다.
- getValues() : 폼 전체 값을 읽음
- getValues('test') : 폼 안의 개별 인풋 값을 읽는다. (name)에 따라
- getValues(['test', 'test1']) : 인풋의 name 속성을 지정하여 여러 값을 읽는다.
trigger
(payload?: string | string[]) => Promise<boolean>
폼의 input/select 유효성 검사를 수동으로 트리거 한다
control
object
이 객체는 Controller 컴포넌트를 위해 만들어졌다. 안에는 제어되는 컴포넌트를 React Hook Form에 등록하기 위한 메서드가 담겨있다.
Controller
Component
외부 라이브러리와 조합하여 사용하기 위해 도와주는 역할을 한다
example
import React from "react";
import ReactDatePicker from "react-datepicker";
import { TextField } from "@material-ui/core";
import { useForm, Controller } from "react-hook-form";
type FormValues = {
ReactDatepicker: string;
}
function App() {
const { handleSubmit, control } = useForm<FormValues>();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<Controller
control={control}
name="ReactDatepicker"
render={({ field: { onChange, onBlur, value, ref } }) => (
<ReactDatePicker
onChange={onChange} // send value to hook form
onBlur={onBlur} // notify when input is touched/blur
selected={value}
/>
)}
/>
<input type="submit" />
</form>
);
}