새소식

반응형
Programming

React - ref, forwardRef 사용해 값 전달하기

  • -
반응형

ref

React 에서 ref prop은 HTML Element에 직접 접근하기 위해 사용됩니다.

 

예를 들어, 아래 <Form> 컴포넌트에서는 useRef 훅으로 생성한 inputRef<input> 엘리먼트의 ref prop으로 넘기고 있습니다. 이렇게 해주면 inputRef 객체에 접근해서 current속성에 <input> 엘리먼트에 레퍼런스가 할당되고 이를 통해 handleFocus 이벤트 핸들러에서 <input> 엘리먼트의 focus 함수를 호출할 수 있습니다.

const Form = () => {
  const inputRef = useRef(null)

  const handleFocus = () => {
    inputRef.current.focus()
  }

  return (
    <>
      <input type="text" ref={inputRef} />
      <button onClick={handleFocus}>인풋 포커스</button>
    </>
  )
}

 

자식 컴포넌트에 접근하기

그렇다면 부모컴포넌트에서 자식컴포넌트 내부에 있는 input에 접근 하려면 어떻게 해야 할까요??

const Parent = () => {
  return (
    <>
      <Child/>
    </>
  )
}

const Child = () => {
  return (
    <>
      <input/>
    </>
  )
}

위와 같은 부모/자식 구조에서 Parent에서 어떤 이벤트가 발생했을 때 Child의 input에 focus를 줘야한다면 어떻게 해야 할까요?

 

먼저 부모컴포넌트인 <Parent>useRef 훅 함수로 생성한 inputRef 객체를 자식인 <Child> 컴포넌트에 ref prop으로 넘깁니다. 그러면 자식인 Child 컴포넌트는 이 ref prop으로 넘어온 inputRef 객체를 다시 내부에 있는 <input> 엘리먼트의 ref prop으로 넘겨줍니다.

const Child = ({ ref }) => {
  return <input type="text" ref={ref} />
}

const Parent = () => {
  const inputRef = useRef(null)

  const handleFocus = () => {
    inputRef.current.focus()
  }

  return (
    <>
      <Child ref={inputRef} />
      <button onClick={handleFocus}>인풋 포커스</button>
    </>
  );
}

언뜻 보기에는 괜찮아보이는 위의 코드는 실제 브라우저에서 실행하면 콘솔에서 아래와 같은 에러 메시지를 출력합니다.

Warning: Child: `ref` is not a prop. Trying to access it will result in `undefined` being returned. 
If you need to access the same value within the child component, you should pass it as a different prop.

메시지를 해석해보면 ref는 prop이 아니라서 undefined가 반환 될것이고, 그래서 다른 prop을 사용 해야 한다고 알려주고 있습니다.

또한 가이드도 같이 보여주고 있는데요

const Child = ({ inheritRef }) => {
  return <input type="text" ref={inheritRef} />
}

const Parent = () => {
  const inputRef = useRef(null)

  const handleFocus = () => {
    inputRef.current.focus()
  }

  return (
    <>
      <Child inheritRef={inputRef} />
      <button onClick={handleFocus}>인풋 포커스</button>
    </>
  );
}

가이드와 같이 ref 대신 inheritRef와 같이 다른 이름의 prop를 사용하도록 컴포넌트를 수정하여 이 문제를 해결 할 수 있습니다.

하지만, React에서 공식적으로 ref 라는 props를 지원하는데 굳이 다른 이름으로 전달 할 수 밖에 없는지 의문을 가질수 있습니다.

fowardRef 사용하기

React에서는 위와 같은 상황을 해결하기 위해 forwardRef를 제공합니다.

공식문서에서는 forwarRef를 아래와 같이 설명하고 있습니다.

forwardRef lets your component expose a DOM node to parent component with a ref.
//(의역) forwardRef 는 자식 컴포넌트의 DOM node와 ref를 부모 컴포넌트에 노출 할 수 있도록 해줍니다.

사용방법은 아래와 같습니다.

const Parent = () => {
  const inputRef = useRef<HTMLInputElement>(null)
  
  return (
    <div>
      <Child ref={inputRef}/>
    </div>
  )
}


const Child = React.forwardRef<HTMLInputElement>((_, inputRef) => {
  return (
    <div>
      <input ref={inputRef}/>
    </div>
  )
})

React의 정적 메서드인 forwardRef의 인자로 기존 컴포넌트를 넣어주기만 하면 됩니다.

Child 컴포넌트에서의 매개변수 선언부를 보면 const Child = (_, inpurRef) => {} 와 같은 형태로 두번째 매개변수를 명시했는데, 해당 매개변수가 바로 부모 컴포넌트에서 ref props로 넘겨준 ref 변수가 됩니다. 전달받은 ref 변수를 특정 element와 연결 할 수 있습니다.

 

해당 컴포넌트에서 prop를 받기 위한 타입정의를 했다면 아래와 같이 사용 하면 됩니다.

React.forwardRef<HTMLInputElement, Props>((props, ref) => {
// ...
})

 

마무리

부모 컴포넌트에서 자식 컴포넌트 내부에 DOM을 직접 조작 하는 것은 일반적인 컴포넌트 관계에서는 지양하는 패턴이라고 합니다. 여러 컴포넌트로 나누어서 제작하는 것은 세부 기능을 숨기는 추상화를 하기 위한 목적이 있기 때문에 다른 컴포넌트에서 자식 컴포넌트의 특성을 너무 쉽게 변경 할수 있다면 추상화가 잘 안되어 있다고 볼수 있기 때문입니다.

하지만, input이나 button 처럼 외부에서 직접 DOM을 컨트롤 해야 하는 상황에서는 forwardRef를 사용해서 관리하면 됩니다.

 

Reference

https://velog.io/@dev_2dong/React%EC%9D%98-forwardRef%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-%ED%95%98%EC%9C%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-element-%EC%B0%B8%EC%A1%B0%ED%95%98%EA%B8%B0

https://www.daleseo.com/react-forward-ref/

https://www.daleseo.com/react-refs/

https://beta.reactjs.org/reference/react/forwardRef

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.