React에 input값이 완전히 제어되는 Input Element
👇 state를 value로 넘기고 그 state를 다룰 수 있는 핸들러를 콜백으로 넘깁니다.
const [data,setData] = useState('');
const handleChange = (event) => {
setData(event.target.value)
}
// input의 값은 항상 React state의 값으로 갱신됩니다.
<input value={data} onChange={handleChange} />
React에 input값이 제어되지 않는 Input Element
const inputRef = useRef(); // ref 사용
const addStation = () => {
const newStationName = inputRef.current.value;
...
};
return (
<form onSubmit={addStation}>
<Input
type="text"
labelText="지하철 역 이름을 입력해주세요."
ref={inputRef}
/>
<Button>추가</Button>
</form>
);
비제어 컴포넌트를 사용하면 submit할 때만 value(input)에 접근할 수 있어요.
반면에 제어 컴포넌트는 언제든지 state를 통해 value에 접근할 수 있으므로 실시간 검증이나 입력 강제에 대해서 유연합니다.
💪 keep in mind the React docs recommend going with controlled components for most cases.
useImperativeHandle
이 언급되는 이유가 무엇일까요?useImperativeHandle
는 ref를 사용하여 자식 컴포넌트에서 부모 컴포넌트로 values를 올려줄 수 있어요.
부모 컴포넌트는 바로 그 values를 사용할 수 있고, 다른 자식 컴포넌트한테도 넘겨줄 수 있게 되겠죠.
👇 사용법을 알아봅시다.
import { useState, useRef, forwardRef, useImperativeHandle } from "react";
// 1. 부모의 ref를 props로 받으려면 forwardRef로 감싸야해요.
// 첫번째 인자는 일반 props고 두번째 인자가 ref입니다.
const ChildOne = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
// 2. ChildOne 컴포넌트 내부 state인 count를 ref를 통해 parent 컴포넌트한테 보내줄 수 있어요.
useImperativeHandle(ref, () => ({
count, // 객체로 count를 보내주고 있죠?
}));
const updateCount = () => {
setCount((c) => c + 1);
console.log(count + 1);
};
return <button onClick={updateCount}>Increment</button>;
});
const Parent = () => {
// 이제 부모의 ref.current에는 count라는 속성이 추가되었습니다. (ref.current.count)
const ref = useRef();
return (
<div>
<ChildOne ref={ref} />
<ChildTwo ref={ref} />
</div>
);
};
// ref.current.count로 접근하고 있죠?
const ChildTwo = forwardRef((props, ref) => {
const checkCount = () => console.log("->", ref.current.count);
return <button onClick={checkCount}>Count</button>;
});
export default Parent;
💭 그럼 다시 본론으로 왜 useImperativeHandle을 언급할까요?
form 이 있다면 form에서 해결해줄 수 있어요.
없다면 써서 하나로 묶어버려야죠.
useImperativeHandle
는 순수 리액트 API로 form 상태를 구현할 수 있는 최후의 수단이예요.
useImperativeHandle
로 각 자식 input들의 state, ref를 부모인 form에 넘겨준다면
submit할 때 등록된 모든 state, ref에 접근할 수 있어요.
🤔 근데... form onChange에서도 e.currentTarget으로 내부 input들 element 전부 접근 가능한데 굳이 써야하나 싶기도 하네요
뭐.. 여튼 비제어 컴포넌트는 상태값을 저장하지 않아서 실시간 validation이 힘든데 위처럼 useImerpativeHandle을 사용해서 가능하게 할 수 있을 것 같아요.
1. 포커스, 텍스트 선택영역, 미디어의 재생관리
2. 애니메이션 직접 실행
3. 서드 파티 DOM 라이브러리를 React처럼 사용할때