React.forwardRef

김기훈·2023년 3월 9일
0

리액트

목록 보기
6/10

forwardRef

컴포넌트를 통해 자식 중 하나에 ref를 자동으로 전달하는 기법이다. 일반적으로 애플리케이션 대부분의 컴포넌트에 필요하지는 않다.

사용 방법

일반적으로는 부모의 ref를 사용하는 것은 컴포넌트간 의존성을 생성하기 때문에 잘 사용되지 않고, 주로 버튼, 인풋 등과 같은 다른 컴포넌트를 사용하지 않는 말단 요소에서 많이 사용된다.

부모 요소의 값을 자식에서 전달하는 방식으로 흔히 쓰이는 props를 이용해 전달하면 되지 않을까 하지만 ref는 key와 마찬가지로 react에서 다르게 처리되어 props로 전달되지 않기 때문에 forwardRef 사용이 필수적이다.

// App.js
const InputRef = useRef();
<RefInput ref={InputRef} />


// RefInput.jsx
import { forwardRef } from "react";

// forwardRef로 감싸주고 
// props와 별개로 ref를 인수로 넣어준다.
const RefInput = React.forwardRef((props, ref) => {
  return <input ref={ref} type={"text"} />;
});

고차 컴포넌트에서의 forwardRef

고차 컴포넌트는 모든 props를 래핑된 컴포넌트에 전달하는 것이 원칙이지만, props에 ref는 전달되지 않기 때문에 원하는 대로 작동하지 않는다.

Hook의 등장으로 HOC의 필요성이 낮아졌다. 고럼 훅에서는 ref 전달이 어떤식으로 진행되려나..? 하고 찾아보니 createRef가 useRef로 변화하고 부모의 ref를 전달받는 방식으로 forwardref를 사용하는데는 변화가 없었다.

// app.jsx
import { ChildModal } from "./ChildModal";
import { useParent } from "./useParent";

function App() {
  const { modal, modalRef, toggleModal, handleOutsideClick } = useParent();
  return (
    <div className="page" onClick={() => handleOutsideClick()}>
      <button type="button" onClick={() => toggleModal()}>
        Open modal
      </button>
      {modal && <ChildModal ref={modalRef} toggleModal={toggleModal} />}
    </div>
  );
}

export default App;


// useParent.js
import { useRef, useState } from "react";

export const useParent = () => {
  const [modal, setModal] = useState(false);
  const modalRef = useRef(null);

  const toggleModal = () => {
    setModal(!modal);
  };

  const handleOutsideClick = (e) => {
    if (modalRef.current && !modalRef.current.contains(e?.target)) {
      setModal(false);
    }
  };

  return { modal, modalRef, toggleModal, handleOutsideClick };
};


// ChildModal.jsx (forwardref 사용)
import { forwardRef } from "react";

export const ChildModal = forwardRef((props, ref) => {
  const { toggleModal } = props;

  return (
    <div className="modal" ref={ref}>
      <button type="button" onClick={toggleModal}>
        Close modal
      </button>
    </div>
  );
});

사용시 주의사항

1. 조건부 사용 금지

forwardRef로 이뤄진 컴포넌트가 존재할 때만 적용하는 경우, 라이브러리 동작 방식을 변경하거나 리액트를 업데이트할 때 문제가 발생할 수 있다.

2. 리액트 개발자 도구 사용시 컴포넌트 이름 표기

리액트 개발자도구에서 해당 컴포넌트를 확인하면 forwardRef로 표기되는 것을 확인할 수 있다. 이름이 없이 보이는 이유는 forwardRef 사용시 익명함수를 이용하기 때문인데, 이를 명확하게 알아보기 위해 다음과 같은 방법으로 이름을 지정해 줄 수 있다.

1) 익명함수 대신 이름을 지정한 함수 사용하기

export const ChildModal = forwardRef(function modalName(props, ref) {
  const { toggleModal } = props;

  return (
    <div className="modal" ref={ref}>
      <p>모달입니다</p>
      <button type="button" onClick={toggleModal}>
        Close modal
      </button>
    </div>
  );
});

2) displayName 속성 이용하기

export const ChildModal = forwardRef((props, ref) => {
// 기존 내용과 동일
});

ChildModal.displayName = "useDisplayName";

3. 말단 컴포넌트에서만 사용하기

해당 속성은 부모의 DOM 노드를 가져와 사용하는 것이기 때문에 중첩해서 사용시 컴포넌트간 결합도가 높아지게 된다. 따라서 가장 말단 컴포넌트에 적용해 컴포넌트간의 결합도를 최소화하여 사용하는 것이 좋다.

profile
평생 공부하기

0개의 댓글

관련 채용 정보