React에 의해 값이 제어되는 컴포넌트를 제어 컴포넌트라고 합니다. 보통 form
이나 input
등의 입력 요소 태그를 다룰 때, 요소에 입력되는 값을 state로 관리하거나 DOM API를 통해서 관리할 수 있습니다. 이때 state로 DOM 요소의 값을 다루는 컴포넌트가 바로 제어 컴포넌트입니다.
아래 코드는 input
의 값이 바뀔 때마다 ChangeName 함수를 통해 state의 값을 업데이트해주는 제어 컴포넌트입니다.
import React, { useState } from "React";
function Control() {
const [name, setName] = useState(null);
const ChangeName = (e) => {
setName(e.current.value);
}
return (
<input onChange={ChangeName} value={name} />
)
}
제어 컴포넌트는 사용자의 입력을 기반으로 state를 관리하고 update합니다. 이처럼 제어 컴포넌트는 React에 의해 값이 제어되는 입력, 폼 요소에서 사용됩니다.
제어 컴포넌트는 입력할 때마다 렌더링 하기 때문에, 불필요하게 리렌더링되거나 API를 호출할 수 있습니다. 즉 사용자가 입력하는 모든 데이터가 동기화됩니다.
이 문제를 해결하기 위해서 쓰로틀링(Throttling)과 디바운싱(Debouncing)을 활용할 수 있습니다.
- 쓰로틀링(Throttling) : 마지막 함수가 호출된 후 일정시간이 지나기 전에 다시 호출되지 않도록 하는 것
- 디바운싱(Debouncing) : 연이어 호출되는 함수들 중 마지막(혹은 맨 처음) 함수만 호출하도록 하는 것
React에 의해 값이 제어되지 않은 컴포넌트를 비제어 컴포넌트라고 합니다. 제어 컴포넌트에서 폼 데이터는 React 컴포넌트에서 다뤄지는 반면, 비제어 컴포넌트는 DOM 자체에서 폼 데이터가 다뤄집니다.
따라서 모든 state 업데이트에 대한 이벤트 핸들러를 작성하는 대신 ref
를 사용해 DOM에서 폼 값을 가져올 수 있습니다.
아래 코드는 ref를 통해 input 값에 접근 할 수 있고, 이벤트 핸들러를 통해 ref에 저장된 요소의 값을 가져와 활용하는 비제어 컴포넌트입니다.
import React, { useRef } from "React";
function UnControl() {
const nameRef = useRef(null);
return (
<input ref={nameRef} />
)
}
비제어 컴포넌트는 state로 값을 관리하지 않기 때문에 값이 바뀔 때마다 리렌더링, API 호출을 하지 않아 성능상 이점이 있습니다. 즉 비제어 컴포넌트는 사용자가 직접 트리거 하지 전까지는 리렌더링을 발생시키지 않고 값을 동기화 시키지도 않습니다.
대표적 예로 submit 버튼을 클릭하면 함수 내에서 ref를 통해 form 내 value들에 접근합니다.
왜 비제어 컴포넌트를 사용할 때 useRef를 사용하고, useRef는 리렌더링을 발생시키지 않을까?
1. useRef()는 heap 영역에 저장되는 일반적인 자바스크립트 객체이다.
2. 메번 렌더링할 때 동일한 객체를 제공한다. heap에 저장되어 있기 때문에 어플리케이션이 종료되거나 가비지 컬렉팅될 때까지, 참조할 때마다 같은 메모리 값을 가진다고 할 수 있다.
3. 값이 변경되어도 리렌더링 되지 않는다. 같은 메모리 주소를 가지고 있기 때문에 자바스크립트의 일치 연산(===)이 항상 true를 반환한다.→ 즉 변경을 감지할 수 없어 리렌더링 하지 않는다.
일반적으로 모든 form 요소의 동기화가 필요하지 않고, form 요소가 증가할수록 면 모든 컴포넌트에 쓰로틀링이나 디바운싱을 걸기엔 어려움이 있습니다. 따라서 만약 값이 트리거 된 이후에 값이 갱신되어도 된다면, 비제어 컴포넌트를 통해 불필요한 렌더링을 방지하고 성능 향상을 할 수 있습니다.
대표적으로 비제어 컴포넌트를 사용해 렌더링을 최적화하는 라이브러리로 react-hook-form
이 있습니다.
제어 컴포넌트 | 비제어 컴포넌트 | |
---|---|---|
1. 동기화 | 항상 동기화(제어 컴포넌트의 값은 항상 최신값을 유지) | 동기화 X |
2. 폼 데이터 | React 컴포넌트 | DOM 자체 |
✔️ React 공식 문서에 따르면 대부분의 경우 폼 구현 시 제어 컴포넌트를 사용하는 것을 권장
기능 | 제어 컴포넌트 | 비제어 컴포넌트 |
---|---|---|
일회성 정보 검색 (ex. 제출) | ✅ | ✅ |
제출 시 값 검증 | ✅ | ✅ |
실시간으로 필드값의 유효성 검사 | ✅ | ❌ |
조건부로 제출 버튼 비활성화(disabled) | ✅ | ❌ |
실시간으로 입력 형식 적용하기(ex. 숫자만 가능하게 등) | ✅ | ❌ |
동적 입력 | ✅ | ❌ |
📎 참고문서