React

forwardRef : 컴포넌트 외부에서 내부 요소 조작, 트리거 하기

vamosdev12 2025. 5. 20. 12:01

[적용 상황]

어떤 자식요소의 트리거를 위해 부모요소에서 특정 상태 또는 값을 업데이트 하니,

다른 자식요소(형제요소)까지 불필요하게 리렌더링 되는 현상 발생

= > 이를 해결하기 위해 "자식요소의 트리거를 자식요소 내부에서 선언하고 그 트리거만 외부로 전달하도록 함"

 

[사용방법]

1. 자식요소에서 컴포넌트를 forwardRef()로 감싼 후, props 옆에 ref를 넣는다.

   (ref는 임의의 명칭이 아니며, 다른 이름으로 바꿀 수 없다. 즉, 반드시 'ref'라고 기재해야 complile이 가능하다)

2. useImperativeHandle을 이용하여 필요한 트리거 함수를 선언한다.

  ( useImperativeHandle: ref로 노출할 메서드나 값을 사용자 정의할 수 있게 해줌, 보통 ref를 붙이면 DOM 노드만 참조되지만, 이 훅을 사용하면 원하는 메서드를 직접 노출하는 것이 가능!)

// 참고: forwardRef의 타입 선언시 ref의 타입을 무조건 먼저 선언해야 함!
export const ChildrenComponent = forwardRef(
  ({ title }, ref) => {

    const [currentTime, setCurrentTime] = useState(getDateHHMMss());

    useImperativeHandle(ref, () => ({
      updateTime: () => setCurrentTime(getDateHHMMss())
    }));

    return (
      <article>
        <h3>{title}-{currentTime}</h3>
      </article>
    );
  }
);

 

3. 부모요소에서 해당 ref를 받을 ref를 생성한다.

4. 자식요소의 'ref' 속성의 값으로 전달한다.

5. 필요한 부분에서 선언해둔 함수를 호출한다.

export const ParentComponent = () => {
  const titleRef = useRef(null);

  useEffect(() => {
    const timer = setInterval(() => {
      titleRef.current?.updateTime(); // ref의 속성으로 호출!
    }, 5000);
    return () => clearInterval(timer);
  }, []);

  return (
    <div>
      <SectionTitleComponent
        ref={titleRef} // ref의 속성값으로 전달(실은 받는 것)
        title="매장 리스트"
      />
    </div>
  );
};

 

 

[참고]

- 이번에는 useImperativeHandle를 사용하여 함수(트리거)만 넘겼지만, DOM요소에 접근하도록 ref에 실어서 보낼 수도 있다.

- 외부에서 input에 포커스를 주거나 값을 가져오고 싶을 때 매우 유용하다고 한다.

// Children.tsx
// ref를 input 태그에 직접 연결
const InputComponent = forwardRef<HTMLInputElement>((props, ref) => {
  return (
    <input
      type="text"
      ref={ref}
      placeholder="입력하세요"
      className="border p-2 rounded"
    />
  );
});

export default InputComponent;

 

// Parents.tsx
const ParentComponent = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  const handleFocus = () => {
    inputRef.current?.focus(); // 내부 input DOM에 접근 가능
  };

  const handleSetValue = () => {
    if (inputRef.current) {
      inputRef.current.value = "Hello from Parent!";
    }
  };

  return (
    <div>
      <InputComponent ref={inputRef} />
      <button onClick={handleFocus}>포커스 주기</button>
      <button onClick={handleSetValue}>값 설정</button>
    </div>
  );
};

export default ParentComponent;