이 글은 책 리액트를 다루는 기술을 개인적으로 정리한 글 입니다.
HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 ref(reference의 줄임말) 사용
특정 DOM에 작업을 해야 할 때 ref를 사용 -> DOM을 꼭 직접적으로 건드려야 할 때
state만으로 해결할 수 없는 기능
• 특정 input에 포커스 주기
• 스크롤 박스 조작하기
• Canvas 요소에 그림 그리기 등
이때는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데, 이를 위해 바로 ref를 사용
ref를 사용하는 방법은 두 가지
ref를 만드는 가장 기본적인 방법 -> 콜백 함수 사용
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달
이 콜백 함수는 ref 값을 파라미터로 전달받음 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정
<input ref={(ref) => {this.input=ref}} />
이렇게 하면 앞으로 this.input은 input 요소의 DOM을 가리킴
ref의 이름은 원하는 것으로 자유롭게 지정할 수 있음
DOM 타입과 관계없이 this.superman = ref처럼 마음대로 지정
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것
이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있음
import React, { Component } from "react";
class RefSample extends Component {
input = React.createRef();
handleFocus = () => {
this.input.current.focus();
}
render() {
return (
<div>
<input ref={this.input} />
</div>
);
}
}
export default RefSample;
createRef를 사용하여 ref를 만들려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 줌
그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어 주면 ref 설정이 완료
설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회
콜백 함수를 사용할 때와 다른 점은 이렇게 뒷부분에 .current를 넣어 주어야 한다는 것
리액트에서는 컴포넌트에도 ref를 달 수 있음
이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 씀
// DOM에 ref를 다는 방법과 똑같음
<MyComponent
ref={(ref) => {this.myComponent=ref}}
/>
이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근 가능 -> 즉, 내부의 ref에도 접근할 수 있음
(예: myComponent.handleClick, myComponent.input 등)
// 스크롤 박스가 있는 컴포넌트를 하나 만들고, 스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행
// 최상위 DOM에 ref를 달기
import React, { Component } from 'react';
class ScrollBox extends Component {
// 컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드
// 이렇게 만든 메서드는 부모 컴포넌트인 App 컴포넌트에서 ScrollBox에 ref를 달면 사용가능
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
/* 앞 코드에는 비구조화 할당 문법을 사용했습니다.
다음 코드와 같은 의미입니다.
const scrollHeight = this.box.scrollHeight;
const clientHeight = this.box.cliengHeight;
*/
this.box.scrollTop = scrollHeight - clientHeight;
};
render() {
const style = {
border: '1px solid black',
height: '300px',
width: '300px',
overflow: 'auto',
position: 'relative'
};
const innerStyle = {
width: '100%',
height: '650px',
background: 'linear-gradient(white, black)'
};
return (
<div
style={style}
ref={ref => {
this.box = ref;
}}
>
<div style={innerStyle} />
</div>
);
}
}
export default ScrollBox;
자바스크립트로 스크롤바를 내릴 때는 DOM 노드가 가진 다음 값들을 사용
• scrollTop: 세로 스크롤바 위치(0~350)
• scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
• clientHeight: 스크롤이 있는 박스의 높이(300)
scrollHeight에서 clientHeight 높이를 빼면 -> 스크롤바를 맨 아래쪽 이동
주의할 점
문법상으로는 onClick = {this.scrollBox.scrollBottom} 같은 형식으로 작성해도 틀린 것은 아님
하지만 컴포넌트가 처음 렌더링될 때는 this.scrollBox 값이 undefined이므로
this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생
화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고 그 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면 버튼을 누를 때
(이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점) this.scrollBox.scrollToBottom 값을 읽어 와서 실행(오류발생 X)
컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용
ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 반드시 고려한 후에 활용해야 함
이 시점에서 오해할 수 있는 부분이 있는데, 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용한다면 이는 잘못 사용된 것
컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류