React
의 데이터 흐름은 기본적으로 props
를 통해 부모 컴포넌트가 자식에게 데이터를 전달하는 방식으로 진행된다.
이런 흐름에서 벗어나 자식 엘리먼트 혹은 컴포넌트에 직접 접근이 필요한 경우가 있는데, Ref 객체
와 ref prop
을 이용하면 자식의 참조를 전달받아 노드에 접근이 가능하다.
어떻게 사용하는지 아래에서 자세히 알아보자!
Ref
를 사용하기 위해선 먼저 Ref 객체
생성이 필요하다.
React.createRef()
React.createRef()
메서드는 Ref 객체
를 생성해서 반환한다.
Ref 객체
를 생성했다면, 아래처럼 접근하려는 엘리먼트 혹은 컴포넌트의 ref prop
에 Ref 객체
를 부착하기만 하면 된다.
class App extends React.Component {
constructor(props) {
super(props);
this.title = React.createRef();
}
render() {
return (
<h1 ref={this.title}>Hello World!</h1>
);
}
}
이제 App
컴포넌트는 this.title
프로퍼티를 이용해 노드에 접근이 가능해진다.
생명 주기(Lifecycle) 에서 도표를 보면 알 수 있듯이, ref
업데이트는 render()
와 componentDidMount()
메서드 호출 사이에 완료된다.
그럼 정상적으로 노드에 접근한지 확인하기 위해 componentDidMount()
메서드 안에서 this.title
프로퍼티에 저장된 Ref 객체
를 콘솔에 출력해보자.
위 코드를 통해 Ref 객체
를 콘솔에 출력한 결과는 아래와 같다.
출력 결과를 보면 Ref 객체
의 current
프로퍼티 안에 h1
엘리먼트가 담겨있는 것을 볼 수 있다.
즉, ref prop
을 통해 넘겨받은 노드는 Ref 객체
의 current
프로퍼티를 통해 접근이 가능하다.
여기까지 기본적인 Ref
사용방법이였다. 좀 더 자세한 내용은 아래에서 확인하자!
current
프로퍼티는 ref prop
이 어디서 사용되었는지에 따라 다른 값을 가진다.
크게 HTML 엘리먼트
, 클래스 컴포넌트
, 함수 컴포넌트
로 분류되는데, 어떤 값을 가지는지 자세히 알아보자.
HTML 엘리먼트
에서 사용되었다면, current
프로퍼티는 DOM Element
를 참조한다.
버튼을 클릭하면 input element
에 포커스를 하는 간단한 예제를 통해 확인하자.
버튼에 textFocus()
메서드를 이벤트 핸들러로 추가했다.
textFocus()
메서드는 ref
로 참조하고 있는 DOM Element
의 focus()
함수를 호출한다.
실행 결과는 위와 같다.
ref prop
이 클래스 컴포넌트
에서 사용됐다면, current
프로퍼티는 Class
의 인스턴스를 참조한다.
역시 간단한 예제를 통해 확인해보자.
App
컴포넌트는 MyComponent
컴포넌트를 호출할 때 ref prop
으로 자신의 Ref 객체
를 부착해서 호출하고 있다.
그러면 App
컴포넌트의 Ref.current
프로퍼티는 MyComponent
컴포넌트의 인스턴스를 참조하게 되고, App
컴포넌트에서 MyComponent
컴포넌트의 메서드 호출이 가능해진다.
App
컴포넌트에서 callRefMethod()
메서드를 이용해 activateAlert()
메서드를 호출하는 것을 볼 수 있는데, 실제로 작동하는지 한 번 확인해보자.
App
컴포넌트에서 MyComponent
의 메서드 호출이 가능한 것을 볼 수 있다.
함수형 컴포넌트는 인스턴스가 존재하지 않기 때문에 ref prop
을 이용한 참조가 불가능하다.
어디까지나 ref prop
으로 함수형 컴포넌트를 참조하는 것이 불가능한 것이지, 함수형 컴포넌트에서 Ref 객체
의 사용은 가능하기 때문에 헷갈리지 않도록 주의하자.
callback ref
는 ref
가 설정되거나 혹은 해제될 때, 세부 사항을 구현하기 위해 사용한다.
기존의 ref
와는 다르게 createRef()
메서드를 이용해 Ref 객체
를 생성하지 않고도 참조가 가능하며, Ref 객체
를 사용하는 것이 아니기 때문에 current
프로퍼티도 사용하지 않는다.
또한 ref prop
에 등록된 callback ref
는 컴포넌트가 마운트될 때, 인자로 엘리먼트 혹은 컴포넌트를 전달하고 마운트 해제시 인자로 null
을 전달해서 호출한다.
그럼 간단한 예제를 통해 알아보자.
App
컴포넌트의 메서드 부터 차근차근 살펴보자.
setRef(node)
callback ref
메서드로, 인자로 전달된node
를 컴포넌트의myRef
에 대입하고, 마운트가 해제되어node
를null
로 호출하게 되면alert()
함수를 호출한다.
focusInput()
이벤트 핸들러이며,myRef
가 참조하고 있는 엘리먼트를 포커스 한다.
unmountComponent()
이벤트 핸들러이며,App
컴포넌트가 렌더링 되는DOM
컨테이너에 다른 엘리먼트를 렌더링해서App
컴포넌트를 마운트 해제 시키는 역할을 한다.
정리하면 Focus
버튼을 누르면 callback ref
를 이용해 참조하고 있는 엘리먼트를 포커스 하고, Unmount
버튼을 누르면 App
컴포넌트의 마운트를 해제시킨다.
이제 결과를 확인해보자.
정상적으로 동작하는 것을 볼 수 있다.
이처럼 callback ref
는 기존의 Ref
와 다른점이 많으니 특징을 잘 기억해두자.
ref 전달하기는 단어 그대로 Ref
객체를 더 하위 트리에 있는 컴포넌트로 전달하는 방법이다.
React.forwardRef()
메서드를 이용해 Ref 객체
를 전달할 수 있으며, 문법은 아래와 같다.
React.forwardRef(callback)
callback
함수의 내용은 다음과 같다.
(props, ref) => { ReactNode }
props
와 ref
객체를 전달받고 ReactNode
를 반환한다.
React.forwardRef(callback)
함수는 쉽게 말해 ref
전달이 가능한 컴포넌트를 생성한다.
간단한 예제를 보며 이해해보자.
Parent
컴포넌트에서 Ref 객체
를 생성해서 Child
컴포넌트의 ref prop
에 부착하였다.
Child
가 보통의 클래스 컴포넌트라면 parentRef.current
프로퍼티는 Child
컴포넌트의 인스턴스를 가리켜야 하지만, 콘솔에 출력된 결과를 보면 다음과 같다.
Child
컴포넌트의 input element
를 가리키는 것을 볼 수 있다.
이 말은 Parent
컴포넌트의 버튼을 클릭하면 Parent
컴포넌트의 자식인 Child
컴포넌트의 input element
에 포커스가 가능하다는 얘기다.
정상적으로 동작하는지 직접 확인해보자.
의도한 대로 동작하는 것을 볼 수 있다.
이렇게 React.forwardRef()
메서드를 이용하면 Ref 객체
를 트리 하부로 전달하는 것이 가능해진다.
아래 예제처럼 조건부 렌더링을 사용할 때, Ref.current
프로퍼티가 참조하는 노드가 존재하지 않는 경우도 있다.
초기 상태는 상관 없지만, 만약 toggleFlag
버튼을 누른 뒤에 Focus
버튼을 누르면 에러가 발생할 수 있다.
그렇기 때문에 current
프로퍼티를 사용하는 함수는 아래처럼 유효성 검사를 진행하는 조건문을 넣어주자.
callback ref
함수를 인라인으로 선언했다면 컴포넌트 업데이트가 이루어질 때, 처음엔 null
을 인자로 하고 다음은 엘리먼트 혹은 컴포넌트를 인자로 해서 두 번 호출된다.
이유는 매 렌더링마다 새로운 callback
인스턴스가 생겨서 이를 초기화하기 위함인데, callback ref
를 클래스에 메서드로 바인딩시켜서 해결할 수 있다.
참고 자료
Ref와 DOM - React
https://ko.reactjs.org/docs/refs-and-the-dom.html
Forwarding Refs - React
https://ko.reactjs.org/docs/forwarding-refs.html
React 최상위 API - React
https://ko.reactjs.org/docs/react-api.html