“제어되지 않은” 몇몇 지역 state를 갖는 컴포넌트를 사용하는 것은 흔한 일입니다. 예를 들어 isActive state를 갖는 원래의 Panel 컴포넌트는 해당 컴포넌트의 부모에서 패널의 활성화 여부에 영향을 줄 수 없기 때문에 제어되지 않습니다.
반대로 컴포넌트의 중요한 정보가 자체 지역 state 대신 props에 의해 만들어지는 경우 컴포넌트가 “제어된다”고 합니다. 이를 통해 부모 컴포넌트가 동작을 완전히 지정할 수 있습니다.
isActive prop을 갖는 최종 Panel 컴포넌트는 Accordion 컴포넌트에 의해 제어됩니다.
비제어 컴포넌트는 설정할 것이 적어 부모 컴포넌트에서 사용하기 더 쉽습니다. 하지만 여러 컴포넌트를 함께 조정하려고 할 때 비제어 컴포넌트는 덜 유연합니다. 제어 컴포넌트는 최대한으로 유연하지만, 부모 컴포넌트에서 props로 충분히 설정해주어야 합니다.
실제로 “제어”와 “비제어”는 엄격한 기술 용어가 아니며 일반적으로 컴포넌트는 지역 state와 props를 혼합해서 사용합니다. 그러나 이런 구분은 컴포넌트의 설계와 제공하는 기능에 관해 설명하는데 유용한 방법입니다.
컴포넌트를 작성할 때 어떤 정보가 (props를 통해) 제어되어야 하고 어떤 정보가 (state를 통해) 제어되지 않아야 하는지 고려하세요. 그렇지만 언제든 마음이 바뀔 수 있고 나중에 리팩토링 할 수 있습니다.
위의 내용은 React Docs에서 제어컴포넌트와 비제어 컴포넌트에 대해 간략하게 설명하고 있는 글귀이다. 이것만으로는 우리가 이게 무엇인지 알기 힘들다.
제어 컴포넌트는 React에 의해 값이 제어되는 컴포넌트를 말한다. 우리가 form
이나 input
요소를 다룰 때 입력값을 state로 관리하거나 DOM API를 통해 관리할 수 있는데 state로 관리하는 컴포넌트를 제어 컴포넌트라고 할 수 있다.
import React, { useState } from 'react';
function MyInput() {
const [inputValue, setInputValue] = useState(null);
const handleChange = (e) => {
setInputValue(e.target.value);
};
return <input onChange={(e) => handleChange(e)} value={inputValue} />;
}
우리가 흔히 리액트로 입력을 관리할 때 사용하는 예시 코드이다. handleChange 함수를 통해 state를 업데이트하여 입력값을 제어하고 있다. 이때 React, ReactDOM, 사용자가 보는 화면이 모두 동기화가 되어야 한다.
import React, { useRef } from 'react';
function Test() {
return <MyInput />;
}
function MyInput() {
const inputNode = useRef(null);
return <input ref={inputNode} />;
}
export default MyInput;
반대로 비제어 컴포넌트는 state를 사용하지 않아 값이 업데이트가 되어도 리렌더링이 되지않고 input값은 사용자만 컨트롤이 가능하며 값이 필요한 시점에 ref
에 저장된 엘리먼트의 값을 가져와서 사용한다.