리액트로 폼을 다루는 방식은 제어 컴포넌트 방식, 비제어 컴포넌트 방식으로 나뉜다.
react-hook-form을 공부할 때, register 함수가 ref를 캡쳐하여 비제어 컴포넌트 방식으로 form을 다루어 리렌더링의 횟수가 줄어들며 성능의 이점을 가진다고 하였다.
여기서 비제어 컴포넌트, 제어 컴포넌트가 무엇인지 몰라 따로 정리하는 포스트를 작성한다.
클라이언트가 정보를 입력·선택하고, 웹 서버 등의 폼을 처리하는 곳으로 제출하기 위한 도구
설명과 같이 Form Element는 클라이언트가 정보를 입력하여, 내부에 자체 state를 가진다.
폼 엘리먼트의 상태를 그대로 사용하는 것이 비제어 컴포넌트 방식, 리액트의 state가 폼 상태에 관여를 한다면 제어 컴포넌트 방식이라고 부른다.
form element를 리액트가 제어하는 방식이다. 코드를 살펴보면 다음과 같다.
const [name, setName] = useState('');
return(
<form onSubmit={addStation}>
<Input
type="text"
labelText="이름을 입력해주세요."
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Button>제출</Button>
</form>
);
사용자가 input에 입력을 하면 onChange 함수가 실행되고, 콜백함수인 setter가 동작하여 name state를 바꾸며 input에는 새로운 value가 할당된다.
동작하는 순서는 다음과 같다.
1. 사용자가 input element에 값을 입력한다.
2. element에 새로운 data가 입력될때마다(onChange가 발생할 때마다) setState를 통해 state를 변경한다.
3. input element의 value에는 변경된 state를 할당한다.
제어 컴포넌트 방식은 react state가 input element의 value를 결정한다.
비제어 컴포넌트 방식에서는 DOM에 직접 접근하는 ref를 통해 form element에 접근한다.
const inputRef = useRef(); // ref 사용
const addName = () => {
const name = inputRef.current.value;
/* .. */
};
return (
<form onSubmit={addName}>
<Input
type="text"
labelText="이름을 입력해주세요."
ref={inputRef}
/>
<Button>제출</Button>
</form>
);
버튼을 누르면 onSubmit에 등록된 addName이 실행되고, addName 함수 내에서 ref를 통해 form에 등록된 value에 직접 접근한다.
제어 컴포넌트 방식은 클라이언트가 input에 값을 입력할 때마다 state가 바뀐다. 즉 실시간으로 state를 확인할 수 있으므로 Validation 검사에 더 유용하다.
하지만, state가 바뀐다는 것은 곧 리렌더링이 발생하는 것이므로 경우에 따라서는 성능의 저하를 불러올 수 있다.
비제어 컴포넌트 방식은 버튼을 누르는 최종 단계에서만 ref를 통해 form value에 접근하므로 리렌더링이 자주 발생하지 않는다. 하지만, 그만큼 실시간으로 value를 확인하기는 어렵다.
state로 form의 값을 관리하는 제어 컴포넌트의 경우 값이 입력될 때마다 state가 바뀌어 리렌더링이 일어난다.
그런데 react-hook-form은 form을 비제어 컴포넌트로 관리하여(ref를 통해 직접 DOM 조작) 값이 바뀌어도 리렌더링이 발생하지 않아 성능상의 이점을 가진다. 거기다 실시간으로 값을 확인하는 메서드도 존재하므로, 결국 비제어 컴포넌트와 제어 컴포넌트의 이점을 모두 가진다.