forwardRef란?React에서 ref는 기본적으로 DOM 요소에만 연결할 수 있음
컴포넌트 자체에 ref를 연결하려면 forwardRef로 감싸야 함
const MyComponent = forwardRef((props, ref) => {
// ref 사용 가능
});
useImperativeHandle이란?useImperativeHandle은 전달받은 ref에 대해 외부에서 호출할 수 있는 메서드나 값을 정의할 수 있도록 해줌.
useImperativeHandle(ref, () => ({
focusInput: () => {
inputRef.current?.focus();
}
}));
이 방식은 컴포넌트 내부 구현을 숨기고, 외부에 필요한 기능만 노출하는 데 유용.
forwardRef + useImperativeHandle 전체 예시const dialogRef = useRef<DialogRefType>(null);
const handleClick = () => {
dialogRef.current?.focusMenuName();
};
return <CreateMenuDialog ref={dialogRef} />;
interface DialogRefType {
focusMenuName: () => void;
}
const CreateMenuDialog = forwardRef<DialogRefType, Props>((props, ref) => {
const menuNameInputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focusMenuName: () => {
menuNameInputRef.current?.focus();
}
}));
return (
<TextField inputRef={menuNameInputRef} label="메뉴명" />
);
});
Controller는 inputRef prop을 직접 받지 않음.
따라서 render 함수 내에서 field.ref와 직접 연결해야 함.
const inputRef = useRef<HTMLInputElement>(null);
<Controller
name="menuName"
control={control}
render={({ field }) => (
<TextField
{...field}
inputRef={(el) => {
field.ref(el); // RHF가 인풋을 제어할 수 있도록 연결
inputRef.current = el; // 사용자 정의 ref 저장
}}
/>
)}
/>
forwardRef를 써야 컴포넌트에 ref 전달 가능useImperativeHandle로 외부에 필요한 동작만 노출Controller와 함께 쓸 땐 inputRef는 직접 연결해야 하며, field.ref와 동시에 바인딩해야 함...field 안에 ref가 이미 포함되어 있음<Controller
name="menuName"
control={control}
render={({ field }) => (
<TextField {...field} label="메뉴명" />
)}
/>
...field 안에는 다음이 포함되어 있음:valueonChangeonBlurref ← 이게 핵심!따라서 inputRef를 따로 지정하지 않으면 MUI 내부에서 <input ref={...} />로 연결되며, RHF는 정상 작동함.
inputRef={(el) => ...}를 지정하게 되면?<TextField
{...field}
inputRef={(el) => {
field.ref(el); // 꼭 다시 연결해줘야 함!
inputRef.current = el; // 외부 제어용 저장
}}
/>
이때는 MUI가 RHF의 ref를 더 이상 자동 연결해주지 않음.
➡ 우리가 수동으로 field.ref(el)을 꼭 호출해야 RHF가 제대로 작동함.
| 사용 방식 | RHF 연결 여부 |
|---|---|
...field만 사용 | ✅ 자동 연결 |
inputRef를 따로 지정 | ❌ 수동 연결 필요 (반드시 field.ref(el) 호출) |
이 차이를 모르면 reset(), watch(), 유효성 검사가 안 되는 이유를 알 수 없음.