HTML에서 input, textarea, select 와 같은 form element는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트를 한다. React에서는 변경할 수 있는 state가 일반적으로 컴포넌트 state 속성에 유지되며 setState 함수에 의해 업데이트된다. 이러한 방식으로 React에 의해 값이 제어되는 입력 form element를 제어 컴포넌트(controlled component)라고 한다.
공식문서 - https://ko.reactjs.org/docs/forms.html#controlled-components
제어 컴포넌트는 공식문서에 적혀있듯이
사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트 한다
라는 부분은 아래에 코드 예시 처럼 event 객체를 이용해 setState 함수를 이용해 state를 저장하는 방식이다.
const Controlled = () => {
const [test, setTest] = useState<string>("");
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
setTest(value);
};
return <input onChange={onChange} />;
};
export default Controlled;
console로 확인해보니 새로운 값을 입력할 때마다 state를 계속 update해준다.
비제어 컴포넌트는 setState 함수를 사용하지않고 ref를 사용해서 값을 얻는 방식이다.
리액트로 작업하다 보면 Dom 요소에 직접 접근해야 할 때가 있다. 예를 들어 Dom 요소에 Focus
를 주거나 스크롤 위치
를 알고 싶을 때 사용한다. 이 경우에 ref 속성값을 이용하면 컴포넌트 또는 Dom 요소에 직접 접근할 수 있다.
비제어 컴포넌트에서는 useRef를 사용하여 사용자가 직접 트리거 하기 전 까지는 리렌더링을 하지 않는다.
const Uncontrolled = () => {
const ref = useRef<HTMLInputElement>(null);
const onClick = () => {
if (!ref.current) {
return;
}
console.log(ref.current.value);
};
return (
<>
<input ref={ref} />
<button type="submit" onClick={onClick}>
버튼
</button>
</>
);
};
export default Uncontrolled;
버튼을 클릭해야 로그가 찍히고 있고 전송 버튼을 클릭하기 전에는 react에서 state값이 변경되지 않아 리렌더링을 일으키지 않는다.
ref 속성값을 이용해 페이지 렌더링 시에 __input focus__
를 해보자
const InputFocus = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
refFn();
}, []);
const refFn = () => {
if (!inputRef.current) {
return;
}
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button>저장</button>
</div>
);
};
export default InputFocus;
컴포넌트가 생성된 이후라도 ref 객체의 current
속성이 없을 수 있기 때문에 주의해야 한다.
const inputRef = useRef<HTMLInputElement>(null);
const [show, setShow] = useState<boolean>(true);
const showText = () => {
setShow((prev) => !prev);
};
const goToInput = () => {
if (!inputRef.current) {
return;
}
inputRef.current.focus();
};
return (
<div>
{show && <input type="text" ref={inputRef} />}
<button onClick={showText}>텍스트 보이기 / 가리기</button>
<button onClick={goToInput}>텍스트로 이동</button>
</div>
);
};
나는 input을 update하기 위해서 아래와 같이 코드를 만들었지만 각 input에 해당하는 value 값이 바뀌지 않았다
위에 언급했다 시피 input의 value를 처리하기 위해서는 제어 와 비제어 컴포넌트 방식이 있다고 하였고
제어 컴포넌트는 React에 setState 함수에 의해 값이 처리 된다고 하였다.
그렇다면 제어 컴포넌트 방식으로 input을 제어하려고 했던 나는 setList를 통해서 state값을 update 해주어야 하는데 setTest를 통해 해당 state 값을 update 하려고 했으니 기존에 map을 돌려서 뽑아낸 item 요소들의 값이 바뀌지 않았던 것이였다.
제어 컴포넌트 방식 - value 속성 사용
비제어 컴포넌틑 방식 - defaultValue 속성 사용
// list.tsx
const [list, setList] = useState([
{ id: 1, name: "title", value: "제목", label: "title" },
{ id: 2, name: "id", value: "아이디", label: "ID" },
{ id: 3, name: "email", value: "이메일", label: "Email" },
{ id: 4, name: "name", value: "JIN", label: "Name" },
{ id: 5, name: "mobile", value: "010-0000-0000", label: "Mobile" },
{ id: 6, name: "team", value: "F-Lab", label: "Team" },
]);
const [test, setTest] = useState<ListType[]>([]);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value, name } = e.target;
setTest({
...list,
[name]: value,
});
};
const saveForm = () => {
setTest(list);
setEditModal(false);
};
{list.map((item) => (
<Input
key={item.id}
type="text"
value={item.value}
onChange={onChange}
name={item.name}
>
{item.label}
</Input>
))}
setState 함수인 setList를 통해서 각 input에 대한 value를 변경시켜준 뒤에 setTest로 값을 update 시켜준다.
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value, id } = e.target;
const newList = list.map((item) =>
item.id === Number(id) ? { ...item, value } : item
);
setList(newList);
};
const saveForm = () => {
setTest(list);
setEditModal(false);
};
출처 & 참고
https://ko.reactjs.org/docs/uncontrolled-components.html
https://ko.reactjs.org/docs/uncontrolled-components.html
https://velog.io/@jhplus13/react-input%EC%9A%94%EC%86%8C%EC%97%90-value%EC%99%80-defaultValue%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90