일반 HTML에서는 DOM 요소에 고유한 이름을 달 때 id를 사용한다.
특정 id에 특정 스타일을 적용해야 하거나, 자바스크립트에서 해당 id를 가진 요소를 찾아서 작업해야 할 때 등 대개 특정 DOM 요소에 어떤 작업을 해야 할 때 id를 활용한다.
이런 id의 역할을 리액트에서는 ref가 대신한다.
id 대신 ref를 사용하는 이유는 무엇일까? 또 ref는 어떤 작업을 할 때 사용해야 할까?
ref는 render
메서드에서 생성된 DOM 노드나 리액트 엘리먼트에 접근할 수 있도록 레퍼런스 값을 만들어준다. Ref를 통해서 DOM 노드나 리액트 엘리먼트에 접근하여 값을 얻어낼 수 있다.
ref는 DOM을 꼭 직접적으로 건드려야 할 때 사용한다. state만으로 해결할 수 없는 상황에서 유용하다.
물론 리액트 컴포넌트 안에서도 id를 사용할 수는 있다. 하지만 HTML에서 DOM의 id는 유일해야 하는데, 같은 컴포넌트를 여러 번 사용하게 되면 중복 id를 가진 DOM이 여러 개 생기게 되어 에러가 발생하기 때문에 사용하지 않는 편이 좋다.
반면, ref는 id와 같이 전역적으로 작동하는 것이 아니라, 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 발생하지 않는다.
대부분은 id를 사용하지 않고도 구현할 수 있지만 사용해야 하는 상황이 발생할 수 있다. 그럴 때는 컴포넌트를 만들 때마다 id 뒷부분에 추가 텍스트를 붙이면(ex. button01, button02, button03 …) 중복 id 발생을 방지할 수 있다.
클래스 컴포넌트에서는 두 가지 방법으로 ref를 사용할 수 있다.
createRef
를 통한 ref 설정ref를 만드는 가장 기본적인 방법이다.
...
render() {
return <input ref={(ref) => {this.input=ref}} />;
}
}
this.input.focus();
import React, { Component } from "react";
class RefSample extends Component {
render() {
return (
<div>
{/* ref 달아줄 요소에 콜백 함수를 props로 전달해주기 */}
<input ref={(ref) => {this.input=ref}}/>
{/* 접근할 때는 ref 이름으로 조회 */}
<button onClick={() => this.input.focus()}>Click</button>
</div>
);
}
}
export default RefSample;
ref의 이름은 자유롭게 지정할 수 있다.
<button ref={(ref) => {this.cat=ref}}>고양이</button>
<div ref={(ref) => {this.bread=ref}}>빵</div>
createRef
는 리액트 v16.3부터 도입된 내장 함수로, 더 적은 코드로 쉽게 사용 가능하다.
리액트는 컴포넌트가 마운트될 때 current 프로퍼티에 DOM 요소를 할당하고, 컴포넌트가 언마운트될 때 current 프로퍼티에 다시 null을 할당한다.
React.createRef()
를 담아주고,class RefSample extends Class {
input = React.createRef();
...
}
...
render() {
return <input ref={this.input} />;
}
}
.current
를 붙여서 조회한다. this.input.current.focus();
import React, { Component } from "react";
class RefSample extends Component {
// 멤버 변수로 React.createRef() 담아주기
input = React.createRef();
render() {
return (
<div>
{/* ref 달아줄 요소에 ref props로 넣어주기 */}
<input ref={this.input} />
{/* 접근할 때는 멤버 변수 뒷부분에 .current 붙여주기 */}
<button onClick={() => this.input.current.focus()}>Click</button>
</div>
);
}
}
export default RefSample;
DOM에 달 때와 동일한 방법으로 컴포넌트에도 ref를 달 수 있다.
주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓴다.
<MyComponent ref={(ref) => {this.myComponent=ref}} />
외부 컴포넌트에서, 컴포넌트 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
⇒ 즉, 내부의 ref에도 접근 가능 (ex. myComponent.handleClick
, myComponent.input
등)
<MyComponent ref={(ref) => {this.myComponent=ref}} />
<button onClick={() => this.myComponent.handleClick()} />
함수 컴포넌트에서 ref를 사용하려면 useRef
라는 Hook 함수를 사용해야 한다.
useRef
의 사용법은 React.createRef
와 유사하다.
리렌더링 시 클래스형 컴포넌트는
render
함수만 다시 실행하지만, 함수형 컴포넌트는 함수 자체를 다시 실행한다. 이 때문에 함수형 컴포넌트에서createRef
를 사용하면 ref 값이 초기화되어서 원하는 값을 얻지 못하기 때문에 리액트의 Hook인 useRef를 사용하는 것이다.
useRef
를 import 한다.import { useRef } from 'react';
useRef
를 사용하여 Ref 객체를 만든다.const input = useRef();
<input ref={input} />
.current
를 붙여서 조회한다.<button onClick={() => input.current.focus()}>Click</button>
import React, { useRef } from "react";
const RefSample = () => {
const input = useRef();
return (
<div>
<input ref={input} />
<button onClick={() => input.current.focus()}>Click</button>
</div>
);
};
export default RefSample;
컴포넌트 내부에서 DOM에 직접 접근해야 할 때 ref를 사용한다.
클래스형 컴포넌트에서는 콜백 ref나 createRef
라는 내장 함수를 통해 ref를 설정할 수 있고,
함수형 컴포넌트에서는 useRef
라는 Hook을 통해 ref를 설정할 수 있다.
주의
서로 다른 컴포넌트끼리 데이터 교류할 때 ref를 사용하는 것은 옳지 않다. 앱 규모가 커지면 구조가 꼬여버려서 유지 보수가 어려워진다. 컴포넌트들끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류해야 한다. (리덕스 혹은 Context API 등 사용)
참고 자료
김민준, 『리액트를 다루는 기술』, 길벗
React Ref와 DOM