React & React Native/이해하기

[React] forwardRef, useImperativeHandle 이해하고 활용하기

adjh54 2024. 11. 19. 22:45
반응형
해당 글에서는 React에서 forwardRef, useImperativeHandle를 이해하고 활용하는 방법에 대해 알아봅니다.

1) forwardRef


💡forwardRef

- React에서 제공하는 고차 컴포넌트(Higher-Order Component)로 부모 컴포넌트에서 자식 컴포넌트로 ref를 전달할 수 있게 해줍니다.
- 이는 주로 재사용 가능한 컴포넌트 라이브러리를 만들 때 유용하며, 특히 DOM 요소나 클래스 컴포넌트의 인스턴스에 직접 접근해야 할 때 사용됩니다.
 

forwardRef – React

The library for web and native user interfaces

ko.react.dev

 

[ 더 알아보기 ]

💡 forwardRef 선언하는 곳은 부모 컴포넌트인가? 아니면 자식 컴포넌트인가?


- 자식 컴포넌트에서 선언됩니다. 이는 자식 컴포넌트가 부모로부터 전달받은 ref를 사용할 수 있게 해주는 역할을 합니다.
- 자식 컴포넌트에서 forwardRef를 사용하여 컴포넌트를 감싸고, ref를 두 번째 매개변수로 받아 사용합니다.
- 부모 컴포넌트에서는 ref를 생성하고 이를 자식 컴포넌트에 prop으로 전달합니다.

1. forwardRef의 주요 특징


 

특징 설명
ref 전달 부모 컴포넌트에서 생성한 ref를 자식 컴포넌트로 전달할 수 있습니다.
컴포넌트 래핑 기존 컴포넌트를 감싸 새로운 컴포넌트를 생성합니다.
투명한 프로퍼티 전달 ref 이외의 props는 자동으로 래핑된 컴포넌트로 전달됩니다.

 

2. 기본 구조


💡 기본 구조

1. SomeComponent: forwardRef로 생성된 새로운 컴포넌트입니다.
2. forwardRef: React에서 제공하는 고차 컴포넌트(Higher-Order Component)입니다.
3. render: 컴포넌트의 렌더링 로직을 포함하는 함수들 입니다. 이 함수는 props와 ref를 인자로 받습니다
import { forwardRef } from "react";
import { forwardRef, useImperativeHandle } from "react";

// 기본 구조 
const SomeComponent = forwardRef(render)

// 기본 구조 : 상세-1
const SomeComponent = forwardRef(props, ref);

// 기본 구조 : 상세-2
const MyInput = forwardRef(function MyInput(props, ref) {
    return (
        <label>
            {props.label}
            <input ref={ref} />
        </label>
    );
});
});

// 기본 구조 : 상세 -3
export const SomeComponent = forwardRef(({ props }, ref) => {
    return (
        <></>
    )
})
 

forwardRef – React

The library for web and native user interfaces

ko.react.dev

 

 

2) useImperativeHandle


💡 useImperativeHandle

- React Hook으로 부모 컴포넌트에서 자식 컴포넌트의 내부 함수나 값에 접근할 수 있게 해주는 기능을 제공합니다.
- 이를 사용하면 자식 컴포넌트에서 부모 컴포넌트로 특정 기능을 노출시키는 기능을 사용할 수 있습니다.
 

useImperativeHandle – React

The library for web and native user interfaces

ko.react.dev

 

1. 주요 특징


 

특징 설명
forwardRef와 함께 사용 ref를 통해 부모-자식 간 통신을 가능하게 함
선택적 기능 노출 자식 컴포넌트의 내부 구현을 숨기면서 필요한 기능만 노출 가능
직접 상호작용 컴포넌트의 생명주기나 렌더링에 영향을 주지 않고 ref를 통해 직접 상호작용 가능

 

2. 기본 구조


💡기본 구조

1. ref: 부모 컴포넌트에서 전달받은 ref 객체입니다. 이를 통해 부모 컴포넌트가 자식 컴포넌트의 기능에 접근할 수 있게 됩니다.

2. createHandle
: 함수로, 부모 컴포넌트에 노출시키고자 하는 메서드나 속성들을 포함하는 객체를 반환합니다. 이 객체를 통해 자식 컴포넌트의 내부 구현을 숨기면서 필요한 기능만 선택적으로 노출할 수 있습니다.

3. dependencies?
: 선택적 파라미터로, 의존성 배열입니다. 이 배열 내의 값들이 변경될 때만 createHandle 함수가 재실행됩니다. 이를 통해 성능을 최적화할 수 있습니다.
useImperativeHandle(ref, createHandle, dependencies?)

 

 

3. 사용예시


💡 사용예시

- 부모 컴포넌트와 자식 컴포넌트가 있다는 가정하에, 자식 컴포넌트 내에서 선언한 내용입니다.

- 각각 부모 컴포넌트에서 활용가능한 메서드 start, stop, pause으로 정의하였고, useImperativeHandle을 통해서 자식 컴포넌트에서 구성한 메서드와 연결하였습니다.
 /**
   * 부모 컴포넌트로 전달할 값
   */
  useImperativeHandle(ref, () => ({
      start: stopwatchHandler.start,          // 스탑워치를 시작합니다.
      pause: stopwatchHandler.pause,          // 스탑워치를 멈춥니다.
      stop: stopwatchHandler.stop,            // 스탑워치를 종료합니다.
  }));

  const stopwatchHandler = (() => {
      return {
          start: () => console.log("[+] 스탑워치를 시작합니다."),
          stop: () => console.log("[+] 스탑워치를 종료합니다."),
          pause: () => console.log("[+] 스탑워치를 일시정지합니다."),
      }
  })()

 

 

반응형

 

3) forwardRef, useImperativeHandle를 활용한 예시 : 부모/자식 관계


💡 forwardRef, useImperativeHandle를 활용한 예시 : 부모/자식 관계

- forwardRef와 useImperativeHandle을 사용하여 부모 컴포넌트와 자식 컴포넌트 간의 통신을 구현한 예시입니다

 

1. 자식 컴포넌트


💡 자식 컴포넌트

- forwardRef를 사용하여 부모로부터 전달받은 ref를 처리합니다.
- stopwatchHandler 객체를 통해 실제 스톱워치 기능을 구현합니다.
- useImperativeHandle을 사용하여 부모 컴포넌트에서 접근 가능한 메서드(start, pause, stop)를 정의합니다.
import { forwardRef, useImperativeHandle } from 'react';

export const ChildComponent = forwardRef(({ parentProps1, parentProps2 }: { parentProps1: string; parentProps2: string }, ref) => {
		const stopwatchHandler = (() => {
			return {
				start: () => console.log('[+] 스탑워치를 시작합니다.'),
				stop: () => console.log('[+] 스탑워치를 종료합니다.'),
				pause: () => console.log('[+] 스탑워치를 일시정지합니다.'),
			};
		})();
		/**
		 * 부모 컴포넌트로 전달할 값
		 */
		useImperativeHandle(ref, () => ({
			start: stopwatchHandler.start, // 스탑워치를 시작합니다.
			pause: stopwatchHandler.pause, // 스탑워치를 멈춥니다.
			stop: stopwatchHandler.stop, // 스탑워치를 종료합니다.
		}));

		return <div>자식 컴포넌트를 출력합니다.</div>;
	},
);

 

 

2. 부모 컴포넌트


💡 부모 컴포넌트

1. componentRef
- useRef를 사용하여 자식 컴포넌트에 대한 참조(componentRef)를 생성합니다.
- ChildFuncType을 통해 전달 받을 함수를 지정하였습니다.

2. childCallHandler
- 자식 컴포넌트의 메서드(start, stop, pause)를 호출할 수 있는 함수들을 정의합니다.

3. button
- 컴포넌트를 사용하여 사용자가 '시작', '중지', '종료' 버튼을 누르면 자식 컴포넌트의 함수를 호출 할 수 있도록 합니다.
import { useRef } from 'react';
import { ChildComponent } from './ChildComponent';

/**
 * 자식 컴포넌트에서 전달해오는 useImperativeHandle 값
 */
type ChildFuncType = {
	start: () => void;
	stop: () => void;
	pause: () => void;
};

const ParentComponent = () => {
	const componentRef = useRef<ChildFuncType>(null);

	/**
	 * 자식 컴포넌트의 함수를 호출
	 */
	const childCallHandler = (() => {
		return {
			start: () => {
				if (componentRef.current) {
					componentRef.current.start();
				}
			},
			stop: () => {
				if (componentRef.current) {
					componentRef.current.stop();
				}
			},
			pause: () => {
				if (componentRef.current) {
					componentRef.current.pause();
				}
			},
		};
	})();

	return (
		<div>
			<div style={{ marginBottom: 10 }}> 부모 컴포넌트 입니다.</div>
			<div style={{ marginBottom: 10 }}>
				<button onClick={childCallHandler.start}>시작</button>
				<button onClick={childCallHandler.pause}>중지</button>
				<button onClick={childCallHandler.stop}>종료</button>
			</div>
			
			<ChildComponent ref={componentRef} parentProps1='props' parentProps2='props2' />
		</div>
	);
};
export default ParentComponent;

 

 

4) 결과 확인


💡 결과 확인

- 부모 컴포넌트 화면 내에서 자식 컴포넌트 화면이 출력되었고 부모 컴포넌트의 버튼을 눌렀을때, 자식 컴포넌트의 함수가 호출이 되었습니다.

 

 

 

 

 

 

오늘도 감사합니다 😀

 

 

 

반응형