일반 HTML에서 DOM 요소에 이름을 달 때는 id를 사용한다.
많은 사람들이 아래와 같은 구조가 익숙할 것이다.
<div id=“my-element”></div>
이렇게 특정 DOM 요소에 어떤 작업을 해야 할 때 id를 달면 CSS에서 특정 id에 스타일을 적용하거나 JS에서 해당 id를 가진 요소를 찾아 자유롭게 작업할 수 있다.
리액트에서도 HTML에서 id를 사용해 DOM에 이름을 다는 것처럼 ref(reference)를 사용하면 리액트에서도 같은 작업을 할 수 있다.
리액트 컴포넌트에서도 id를 사용할 수는 있지만 특수한 경우가 아니면 사용을 권장하지 않는다
JSX 안에서 DOM에 id를 달면 해당 DOM을 렌더링할 때 그대로 전달되는데, 같은 컴포넌트를 여러 번 사용하는경우 중복 id를 가진 DOM이 여러 개 생기게 된다.
하지만 ref는 전역적으로 작동하지도 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 발생하지 않는다!
앞서 ref는 특정 DOM에 작업을 해야 할 때 사용한다고 얘기했다.
그럼 특정 작업이라는 것은 뭘 의미하는 걸까?
DOM을 직접적으로 꼭 건드려야 할 때
다음 예시를 통해 바닐라 JS 및 jQuery로 만든 웹 사이트에서 input을 검증할 때 특정 id를 가진 input에 클래스를 설정해주는 코드를 살펴보자
<script>
function validate() {
let input = document.getElementById(‘password’);
input.className = ‘’;
if(input.value === ‘0000’) {
input.className = ‘success’;
} else {
input.className = ‘failure’;
}
}
</script>
<body>
<input type=‘password’ id=‘password’></input>
<button onclick=‘validate()’>Validate</button>
</body>
그러나 리액트에서는 state로 구현할 수 있다.
클래스형 컴포넌트에서 ref를 사용하는 방법에 대해 알아보자
함수 컴포넌트에서 사용하려면 Hooks를 사용해야 하므로 추후 Hooks를 배우면서 알아볼 것이다
class ValidationSample extends Component {
state = {
password: ‘’,
clicked: false,
validated: false
}
handleChange = (e) => {
this.setState({
[assword: e.target.value
});
}
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === ‘0000’
})
}
render() {
return (
<div>
<input
type=‘password’
value={this.state.password}
onChange={this.handleChange}
className={this.state.clicked ? (this.state.validated ? ‘success’ : ‘failure’) : ‘’}
/>
<button onClck={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
컴포넌트를 생성했으면 App 컴포넌트에서 해당 컴포넌트를 렌더링 시켜보자
위 예제에서는 state를 사용해서 필요한 기능을 모두 구현했다. 하지만 가끔 state만으로는 해결할 수 없는 기능이 존재한다.
- 특정 input에 포커스 추가
- 스크롤 박스 조작
- Canvas 요소에 그림 그리기 등
이처럼 몇 가지 상황에서는 DOM에 직접적으로 접근해야 하는데, 이때 사용하는 것이 ref 다
이 방법은 ref를 만드는 가장 기본적인 방법이다.
사용법은 ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 주면 된다.
이 콜백 함수는 ref 값을 파라미터로 전달받고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해 준다.
<input ref={(ref) => {this.input=ref}} />
이렇게 하면 this.input은 input 요소의 DOM을 가리킨다.
참고로 this.input에서 input 자리는 원하는 이름으로 지정할 수 있다
두번째 방법은 리액트에 내장되어 있는 createRef 함수를 사용하는 것이다.
이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있다
class RefSample extends Component {
input = React.createRef();
handleFocus = () => {
this.input.current.focus();
}
render() {
return (
<div>
<input ref={this.input} />
</div>
);
}
}
이처럼 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 준 뒤 사용해야 한다.
그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어 주면 ref 설정이 완료된다.
ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 되는데, 콜백 함수를 사용할 때와 다른 점은 뒷부분에 .current를 넣어 줘야 한다는 것이다.
사용방법이 두 가지인 만큼 사용하기 편한 방법으로 사용하면 된다.
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === ‘0000’
});
this.input.focus();
}
<input
ref={(ref) => this.input=ref}
(…)
/>
ref를 이용하여 컴포넌트 내부 메서드를 호출해보자
리액트에서는 컴포넌트에도 ref를 달 수 있다.
이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓴다.
컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.
<MyComponent ref={(ref) => {this.myComponent=ref}} />
이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
👉 내부의 ref에도 접근 가능하다 ex) myComponent.handleClick, myComponent.input 등..
스크롤바를 맨 아래로 내리려면 scrollHeight - clientHeight