본문 바로가기

카테고리 없음

[번역] useImperativeHandle

 

useImperativeHandle – React

The library for web and native user interfaces

react.dev

useImparativeHandle은 ref로 노출된 handle을 사용자 정의할 수 있는 React Hook이다. 

 

useImperativeHandle(ref, createHandle, dependencies?)

 

 

컴포넌트 최상위 레벨에서 useImperativeHandle을 호출하여 노출되는 ref 핸들을 커스터마이징 할 수 있다:

 

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  useImperativeHandle(ref, () => {
    return {
      // ... your methods ...
    };
  }, []);
  // ...

 

- ref: forwardRef 렌더링 함수에서 두 번째 인수로 받은 참조이다.

- createHandle: 인수를 받지 않고 노출하려는 ref핸들을 반호나하는 함수이다. 이 ref 핸들은 어떤 타입이든 가질 수 있다. 일반적으로 노출하려는 메서드가 있는 객체를 반환한다. 

- optional dependencies: createHandle 코드 내부에서 참조된 모든 반응형 값의 목록이다. 반응형 값에는 props, state 컴포넌트 본문에서 직접 선언한 모든 변수와 함수가 포함된다. 린터가 리엑트에 대해 구성된 경우, 모든 반응형 값이 종속성으로 올바르게 지정되었는지 확인한다. 의존성 목록에는 일정한 수의 항목이 있어야 하며 [dep1, dep2, dep3]와 같이 인라인으로 작성해야한다. React는 Object.is 비교를 사용하여 각 의존성을 이전값과 비교한다. 리렌더링으로 인해 일부 의존성이 변경되거나 이 인수를 생량한 경우, createHandle 함수가 다시 실행되고 새로 생성된 핸들이 참조에 할당된다. 

 

useImperativeHandle은 undefined를 반환한다.

 

Exposing a custom ref handle to the parent component

기본적으로 컴포넌트는 부모 컴포넌트에 DOM 노드를 노출하지 않는다. 예를 들어, MyInput의 부모 컴포넌트가 <input> DOM 노드에 액세스하도록 하려면 forwardRef를 사용해야 한다:

 

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  return <input {...props} ref={ref} />;
});

 

위 코드를 사용하면 MyInput에 대한 참조가 <input> DOM 노드를 받게 된다. 하지만 대신 사용자 정의 값을 노출할 수 있다. 노출된 핸들을 사용자 정의하려면 컴포넌트 최상위 레벨에서 useImperativeHandle을 호출할 수 있다. 

 

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  useImperativeHandle(ref, () => {
    return {
      // ... your methods ...
    };
  }, []);

  return <input {...props} />;
});

 

위 코드에는 참조가 더 이상 input으로 전달되지 않습니다:

 

예를 들어 전체 input DOM 노드를 노출하고 싶지 않지만 그 중 두 가지 메서드인 focus와 scrollIntoView를 노출하고 싶다고 가정해보겠습니다. 이렇게 하려면 실제 브라우저 DOM을 별도의 참조에 보관하세요. 그런다음 useImperativeHanlde을 사용해 부모 컴포넌트가 호출할 메서드만 있는 핸들을 노출하자. 

 

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      focus() {
        inputRef.current.focus();
      },
      scrollIntoView() {
        inputRef.current.scrollIntoView();
      },
    };
  }, []);

  return <input {...props} ref={inputRef} />;
});

 

이제 부모 컴포넌트가 MyInput에 대한 참조를 받으면, 이 컴포넌트에서 포커스와 scrollIntoView 메서드를 호출할 수 있게 된다. 그러나 기본 input DOM node에 대한 전체 액세스 권한은 갖지 못한다. 

 

 

Exposing your own imperative methods 

imperative handle을 통해 노출하는 메서드가 DOM 메서드와 정확히 일치할 필요는 없다. 이 Post 컴포넌트는 명령형 핸들을 통해 scrollAndFocusAddCommnet 메서드를 노출한다. 이를 통해 사용자가 버튼을 클릭하면 상위 페이지에서 댓글 목록을 스크롤하고 입력 필드에 초점을 맞출 수 있다. 

 

import { forwardRef, useRef, useImperativeHandle } from 'react';
import CommentList from './CommentList.js';
import AddComment from './AddComment.js';

const Post = forwardRef((props, ref) => {
  const commentsRef = useRef(null);
  const addCommentRef = useRef(null);

  useImperativeHandle(ref, () => {
    return {
      scrollAndFocusAddComment() {
        commentsRef.current.scrollToBottom();
        addCommentRef.current.focus();
      }
    };
  }, []);

  return (
    <>
      <article>
        <p>Welcome to my blog!</p>
      </article>
      <CommentList ref={commentsRef} />
      <AddComment ref={addCommentRef} />
    </>
  );
});

 

 

ref를 과도하게 사용하지 말자. 해당 노드로 스크롤하기, 노드에 포커스 맞추기, 애니메이션 트리거하기, 텍스트 선택하기등과 props로 표현할 수 없는 명령형 동작에만 ref를 사용해야한다. 

 

props로 표현할 수 있다면 ref를 사용해서는 안된다. 예를 들어 모달 컴포넌트에서 {open, close} 와 같은 명령형 핸들을 노출하는 대신, <Modal isOpen={isOpen}/> 처럼 prop을 사용할 수 있다. 이펙트는 프롭을 통해 명령형 동작을 노출하는데 도움이 될 수 있다.