
리액트에서 부모 컴포넌트가 자식 컴포넌트의 HTML요소를 ref로 지정하고, 해당 HTML요소에 접근하여 이벤트를 다루는 방법에 대해 공부한 내용을 정리해보고자 한다.
리액트에서 ref을 prop로 사용하려면 React에서 제공하는 forwardRef를 사용해야한다.
const Parent =()=>{
	const inputRef= useRef<HTMLInputElement>(null);
  //....
  
  return(
  //...
    <MyInput ref={inputRef}/>
  )
}
import React from 'react'
// forwardRef 함수에 제네릭으로 ref와 props의 타입을 명시
 // ref type :HTMLInputElement 
// props  type :React.InputHTMLAttributes<HTMLInputElement>
const MyInput = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  (props, ref) => {
    // input 요소에 ref와 props를 적용하여 반환
    return <input ref={ref} {...props} />
  }
)
const MyInput = React.forwardRef<HTMLInputElement>>(
  (_, ref) => ...
useImperativeHandle 은 리액트 훅 중 하나로, 부모 컴포넌트에게 노출할 ref 핸들을 사용자가 직접 정의할 수 있게 (ref로 노출 시키는 노드의 일부 메서드만 노출할 수 있게) 해주는 훅이다.
useImperativeHandle는 부모 컴포넌트가 ref를 핸들하는 것을 정의하는 것이기 때문에 부모 컴포넌트가 ref에 접근할 수 있도록 해주는 forwardRef와 함께 사용해야한다.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const MyInput = forwardRef((props, ref)=> {
  const inputRef = useRef();
  useImperativeHandle(ref, () => {
    return {
      focus: (f) =>{
         if(f) inputRef.current?.focus();
      },
      scrollIntoView: () => {
        inputRef.current?.scrollIntoView();
      }
    };
  }, []);
  return <input {...props} ref={inputRef} />;
});
const Parent =()=>{
	const inputRef= useRef<InputHandle>(null);
  //....
  	const handleFocus =(f)=>{
    	inputRef.current?.focus(f);
    };
  	const scrollIntoView =()=>{
    inputRef.current?.scrollIntoView()}
  return(
  //...
    <MyInput ref={inputRef}/>
	<button onClick={()=>handleFocus(true)}>focus</button>
	<button onClick={()=>handleFocus(false)}>unFocus</button>
	<button onClick={scrollIntoView}>focus</button>
  )
}
import { forwardRef, useImperativeHandle, useRef } from 'react';
export type InputHandle ={
	focus:(f:boolean)=>void,
  	scrollIntoView :()=>void,
}
const MyInput = forwardRef<InputHandle,React.InputHTMLAttributes<HTMLInputElement> >((props, ref)=> {
  const inputRef = useRef();
  useImperativeHandle(ref, () => {
    return {
      focus: (f:boolean) => {
        if(f) inputRef.current?.focus();
      },
      scrollIntoView: () => {
        inputRef.current?.scrollIntoView();
      },
    };
  }, []);
  return <input {...props} ref={inputRef} />;
});
const Parent =()=>{
	const inputRef= useRef<InputHandle>(null);
  //....
  	const 
  return(
  //...
    <MyInput ref={inputRef}/>
	<button onClick={()=>handleFocus(true)}>focus</button>
	<button onClick={()=>handleFocus(false)}>unFocus</button>
	<button onClick={scrollIntoView}>focus</button>
  )
}
⚠️typescript에서 useImperativeHandle을 사용 시 주의할 점은 js에서 사용할때와 다르기 ref에 대한 타입을 HTMLElement가 아니라 useImperativeHandle로 지정하는 메서드를 속성으로 가지는 타입(ex:InputHandle)으로 지정해주어야한다.