리액트는 두 가지 방식으로 form 입력을 처리한다. 리액트에 의해 입력값이 제어되는 엘리먼트를 제어 컴포넌트 (controlled component)라고 한다. 사용자가 제어 컴포넌트에 데이터를 입력하면 변경 이벤트 핸들러가 호출되고 코드가 업데이트된 값으로 다시 렌더링에 의해 입력의 유효 여부를 결정한다. 다시 렌더링 하지 않으면 form 엘리먼트는 변경되지 않은 상태로 유지된다.
비제어 컴포넌트 (uncontrolled component) 는 form 엘리먼트가 리액트 외부에서 작동하는 것처럼 작동한다. 사용자가 form 필드 (input box, dropdown 등)에 데이터를 입력하면 업데이트된 정보가 리액트에서 별도 처리할 필요 없이 엘리먼트에 반영된다. 그러나 이는 특정 필드가 특정 값을 갖도록 강제할 수 없다는 의미이기도 하다. 대부분 제어 컴포넌트를 사용해야 한다.
1. 제어 컴포넌트
HTML 에서 <input>, <textarea>, <select>와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다. 리액트에서는 변경할 수 있는 state 가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트된다.
리액트의 state를 '신뢰 가능한 단일 출처 (single source of truth)'로 만들어 두 요소를 결합할 수 있다. 그러면 폼을 렌더링 하는 리액트 컴포넌트는 폼에 발생하는 사용자 입력값을 제어한다. 이러한 방식으로 리액트에 의해 값이 제어되는 입력 폼 엘리먼트를 '제어 컴포넌트'라고 한다.
export default function App() {
const [input, setInput] = useState("");
const onChange = (e) => {
setInput(e.target.value);
};
return (
<div className="App">
<input onChange={onChange} />
</div>
);
}
이렇게 사용자의 입력을 받는 컴포넌트에 event 객체를 이용해 setState('')로 값을 지정하는 등 리액트에 의해 값이 제어되는 것을 제어 컴포넌트 제어 방식이다. 제어 컴포넌트는 사용자가 입력한 값과 저장되는 값이 실시간으로 동기화된다.
2. 비제어 컴포넌트
제어 컴포넌트에서 폼 데이터가 리액트 컴포넌트에서 다루어지는 반면, 대안인 비제어 컴포넌트는 DOM 자체에서 폼 데이터가 다루어진다. 모든 state 업데이트에 대한 이벤트 핸들러를 작성하는 대신 비제어 컴포넌트를 만들기 위해서는 ref를 사용하여 DOM에서 폼 값을 가져올 수 있다.
비제어 컴포넌트는 기존의 바닐라 자바스크립트와 크게 다르지 않은 방식이다. 자바스크립트를 사용할 때, 폼을 제출할 때 submit button을 클릭할 때 요소 내부의 값을 얻어왔다. 비제어 컴포넌트 또한 이와 유사한 방식으로 사용된다.
비제어 컴포넌트 방식을 사용할 땐, 제어 컴포넌트 방식에서 사용한 setState()를 쓰지 않고 ref를 사용해서 값을 얻는다.
export default function App() {
const inputRef = useRef(); // ref 사용
const onClick = () => {
console.log(inputRef.current.value);
};
return (
<div className="App">
<input ref={inputRef} />
<button type="submit" onClick={onClick}>
전송
</button>
</div>
);
}
비제어 컴포넌트는 값이 실시간으로 동기화되지 않는다. 만약 a와 b라는 컴포넌트가 있을 때, a에 대한 변화를 즉각적으로 b가 영향을 받아야 할 때 비제어 컴포넌트는 이런 방식에 대한 대응을 할 수 없다.
제어 컴포넌트의 경우 사용자가 입력을 하는 액션을 취할 때마다 리 렌더링을 발생시키는 반면, 비제어 컴포넌트는 사용자가 직접 트리거 하기 전까지는 리렌더링을 발생시키지도 않고 값을 동기화 시키지도 않는다.
그럼 왜 비제어 컴포넌트를 사용할 땐 useRef를 사용하고, 이러한 useRef는 왜 리렌더링을 발생시키지 않는 걸까?
- useRef()는 heap영역에 저장되는 일반적인 자바스크립트 객체이다.
- 매번 렌더링 할 때 동일한 객체를 제공한다. heap에 저장되어 있기 때문에 애플리케이션이 종료되거나 가비지 컬렉팅 될 때까지, 참조할 때마다 같은 메모리 값을 가진다고 할 수 있다.
- 값이 변경되어도 리 렌더링이 되지 않는다. 같은 메모리 주소를 갖고 있기 때문에 자바스크립트의 === 연산이 항상 true를 반환한다. 즉 변경사항을 감지할 수 없어서 리 렌더링을 하지 않는다는 뜻이다.
요약
- 제어 컴포넌트
제어 컴포넌트의 값은 항상 최신 값을 유지한다. 새로운 입력 값이 생길 때마다 상태를 새롭게 갱신한다. 이는 데이터와 UI에서 입력한 값이 항상 동기화됨을 알 수 있다. - 비제어 컴포넌트
필드에서 값을 트리거해야 값을 얻을 수 있다. 사진에선 [전송] 버튼을 클릭하면 console에 값이 찍힌다. [전송] 버튼을 클릭해 트리거하기 전까지의 값은 변경되지 않는다.
언제 사용하면 좋을까?
- 즉각적으로, 실시간으로 값에 대한 피드백이 필요하다 > 제어 컴포넌트 사용
- 즉각적인 피드백이 불필요하고 제출 시에만 값이 필요하다, 불필요한 렌더링과 값 동기화가 싫다 > 비제어 컴포넌트 사용
참고 :
비제어 컴포넌트 – React
A JavaScript library for building user interfaces
ko.reactjs.org
폼 – React
A JavaScript library for building user interfaces
ko.reactjs.org
Controlled and uncontrolled form inputs in React don't have to be complicated - Gosha Arinich
There are many articles saying you should use setState, and the docs claim refs are bad. So contradictory. How are you supposed to make forms?
goshacmd.com
useRef()가 순수 자바스크립트 객체라는 의미를 곱씹어보기
리액트에서는 DOM 노드나 React 엘리먼트에 접근하기 위한 방법으로 Ref라는 API를 제공해왔습니다. class CustomTextInput extends React.Com...
dev.to
React: 제어 컴포넌트와 비제어 컴포넌트의 차이점
👀 제어 컴포넌트와 비제어 컴포넌트의 차이점, 활용 방법에 대해 알아봅시다
velog.io
'TIL' 카테고리의 다른 글
setTimeout 과 setInterval (22.11.17 TIL) (0) | 2022.11.17 |
---|---|
리액트 컴포넌트 (22.11.16 TIL) (0) | 2022.11.16 |
SPA vs MPA (22.11.14 TIL) (0) | 2022.11.14 |
리액트 생명주기 (22.11.13 TIL) (0) | 2022.11.13 |
자바스크립트 동기와 비동기 (22.11.12 TIL) (0) | 2022.11.12 |
댓글