[React] forwardRef

0

React

목록 보기
4/6

forwardRef

forwardRef 는 컴포넌트가 ref와 함께 DOM 노드를 상위 컴포넌트에 노출할 수 있게 합니다.

const SomeComponent = forwardRef(render)

Reference

forwardRef(render)

forwardRef() 를 호출하여 컴포넌트가 ref를 받고, 해당 ref를 자식 컴포넌트로 전달할 수 있게 합니다.

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  // ...
});

Parameters

  • render: 컴포넌트의 렌더 함수입니다. React는 이 함수를 부모로부터 받은 props와 ref와 함께 호출합니다. 반환하는 JSX는 컴포넌트의 출력이 됩니다.

Returns

forwardRef 는 JSX에서 렌더링할 수 있는 React 컴포넌트를 반환합니다. 일반 함수로 정의된 React 컴포넌트와는 달리, forwardRef 에서 반환된 컴포넌트는 ref 프롭을 받을 수 있습니다.

Caveats

  • Strict Mode에서 React는 부수 효과를 찾아주기 위해 렌더 함수를 두 번 호출합니다. 이는 개발용으로만 사용되며 프로덕션에는 영향을 미치지 않습니다. 렌더 함수가 순수 함수(되어야 할 것)인 경우, 이러한 동작이 컴포넌트의 논리에 영향을 미치지 않아야 합니다. 두 번째 호출의 결과는 무시됩니다.

render function

forwardRef는 렌더 함수를 인수로 받습니다. React는 이 함수를 propsref와 함께 호출합니다.

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

Parameters

  • props: 부모 컴포넌트에서 전달된 props입니다.
  • ref: 부모 컴포넌트에서 전달된 ref 속성입니다. ref 는 객체나 함수일 수 있습니다. 부모 컴포넌트에서 ref를 전달하지 않은 경우, ref는 null일 것입니다. 전달받은 ref 를 다른 컴포넌트에 전달하거나 useImperativeHandle 에 전달해야 합니다.

Returns

forwardRef 는 JSX에서 렌더링할 수 있는 React 컴포넌트를 반환합니다. 일반 함수로 정의된 React 컴포넌트와는 달리, forwardRef 에서 반환된 컴포넌트는 ref 프롭을 전달받을 수 있습니다.


Usage

Exposing a DOM node to the parent component

기본적으로 각 컴포넌트의 DOM 노드는 개인적입니다. 그러나 때로는 상위 컴포넌트에 DOM 노드를 노출하는 것이 유용할 수 있습니다. 예를 들어, 포커싱을 가능하게 하기 위해서입니다. 이를 위해 컴포넌트 정의를 forwardRef()로 래핑하세요.

import { forwardRef } from 'react';

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

props 이후 두 번째 인수로 ref를 받게 됩니다. 노출하려는 DOM 노드에 해당 ref를 전달하세요.

import { forwardRef } from 'react';

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

이를 통해 부모 Form 컴포넌트에서 MyInput에 의해 노출된 <input> DOM 노드에 접근할 수 있습니다.3

function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

Form 컴포넌트는 MyInput에 ref를 전달합니다. MyInput 컴포넌트는 해당 ref를 브라우저의 <input> 태그로 전달합니다. 이 결과로, Form 컴포넌트는 해당 <input> DOM 노드에 접근하고 focus()를 호출할 수 있습니다.

컴포넌트 내부의 DOM 노드에 ref를 노출하면 나중에 컴포넌트의 내부를 변경하기가 더 어려워집니다. 일반적으로 버튼이나 텍스트 입력 같은 재사용 가능한 하위 컴포넌트에서 DOM 노드를 노출하지만, 아바타나 댓글 같은 응용 프로그램 수준의 컴포넌트에서는 그렇게 하지 않습니다.


Forwarding a ref through multiple components

DOM 노드로 ref를 전달하는 대신, MyInput과 같은 컴포넌트로 ref를 전달할 수 있습니다.

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

만약 MyInput 컴포넌트가 그것의 <input>으로 ref를 전달한다면, FormField의 ref는 그 <input>을 제공합니다.

function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

Form 컴포넌트는 ref를 정의하고 FormField에 전달합니다. FormField 컴포넌트는 이 ref를 MyInput에 전달하고, MyInput은 브라우저의 <input> DOM 노드로 전달합니다. 이렇게 Form은 그 DOM 노드에 접근합니다.


Exposing an imperative handle instead of a DOM node

전체 DOM 노드를 노출하는 대신, 더 제한된 메서드 집합을 가진 사용자 정의 객체인 imperative handle을 노출할 수 있습니다. 이를 위해서는, DOM 노드를 보유할 별도의 ref를 정의해야 합니다.

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

  // ...

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

전달받은 refuseImperativeHandle에 전달하고, ref에 노출할 값을 지정합니다.

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의 ref를 받는다면, 해당 DOM 노드 대신 { focus, scrollIntoView } 객체만 받게 될 것입니다. 이렇게 함으로써 노출하는 DOM 노드에 대한 정보를 최소한으로 제한할 수 있습니다.

❗️ Pitfall


ref를 과도하게 사용하지 마십시오. ref는 프롭으로 표현할 수 없는 명령형 동작에만 사용해야 합니다. 예를 들어 노드로 스크롤링, 노드에 포커스, 애니메이션 트리거, 텍스트 선택 등입니다.


만약 프롭으로 표현할 수 있다면, ref를 사용해서는 안 됩니다. 예를 들어 Modal 컴포넌트에서 { open, close }와 같은 명령형 핸들을 노출하는 대신 <Modal isOpen={isOpen} />과 같이 isOpen을 프롭으로 사용하는 것이 더 좋습니다. 효과(Effects)를 사용하면 명령형 동작을 프롭을 통해 노출할 수 있습니다.


출처: https://react.dev/reference/react/forwardRef

profile
지치지 않는 백엔드 개발자 김성주입니다 :)

0개의 댓글