일반 HTML에서 DOM 요소에 이름을 달 때는 id를 사용한다.
<div id = "my-element"></div>
리액트 프로젝트에 사용하는 public/index.html 파일에도 id가 root인 div 요소가 존재한다.
public/index.html
<html lang ="en">
...
<body>
<div id ="root"></div>
</body>
</html>
src/index.js 파일 중에는 id가 root인 요소에 리액트 컴포넌트를 렌더링하라는 코드가 있다.
src/index.js
(...)
ReactDOM.render(<App />, document.getElementById('root'));
▶ 리액트 컴포넌트 안에서는 id를 사용하면 안 되나요?
리액트 컴포넌트 안에서도 id를 사용할 수는 있지만 사용을 권장하지 않는다. 예를 들어 같은 컴포넌트를 여러 번 사용해야 할 때 HTML에서 DOM의 id는 유일해야 하는데, 이런 상황에서 중복 id를 가진 DOM이 여러 개 생기니 잘못된 사용이다. → ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 생기지 않는다.
5.1 ref는 어떤 상황에서 사용해야 할까?
→ DOM을 꼭 직접적으로 건드려야 할 때 사용한다.
예를 들어 input의 값을 검증할 때, 리액트에서는 굳이 id를 통해 DOM에 접근하지 않아도 state로 구현할 수 있다.
ValidationSample.js
import React, { Component } from 'react';
import './ValidationSample.css';
class ValidationSample extends Component {
state = {
password: '',
clicked: false,
validated: false
}
handleChange = (e) => {
this.setState({
password: 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 onClick= {this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
- input에서는 onChange 이벤트가 발생하면 handleChange를 호출하여 state의 password값을 업데이트하게 했다.
- button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked 값을 참으로 설정했고, validated 값을 검증 결과로 설정했다.
- input의 className 값은 버튼을 누르기 전에는 비어 있는 문자열을 전달하며, 버튼을 누른 후에는 검증 결과에 따라 success 값 또는 failure 값을 설정한다. 그리고 이 값에 따라 input 색상이 초록색 또는 빨간색으로 나타난다.
▶ DOM을 꼭 사용해야 하는 상황 (state로 해결할 수 없는 기능)
- 특정 input에 포커스 주기
- 스크롤 박스 조작하기
- Canvas 요소에 그림 그리기
- 텍스트 선택영역, 미디어의 재생을 관리할 때
- 애니메이션을 직접 실행시킬 때
- 서드파티 DOM 라이브러리를 React와 같이 사용할 때
→ ref를 사용하여 해결한다.
5.2 ref 사용
1) 콜백함수를 통한 ref 설정
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것이다.
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달 → 콜백 함수는 ref값을 파라미터로 전달받음 → 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정함 → this.input은 input 요소의 DOM을 가리킴
[ 콜백 함수 사용 예시 ]
<input ref = {(ref) => {this.input = ref}} />
2) createRef를 통한 ref 설정
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것이다. (리액트 v16.3 부터 도입)
우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 준 후, 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어 주면 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;
render 메서드 안에서 ref가 엘리먼트에 전달되었을 때, 그 노드를 향한 참조는 ref의 current 속성에 담기게 된다.
→ 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다.
▷ 함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 속성을 사용할 수 없다.
5.3 컴포넌트에 ref 달기
3.1) 사용법
다음은 내부의 ref인 MyComponent 내부의 메서드 및 멤버 변수에 접근할 수 있는 방법이다.
<MyComponent
ref = {(ref) => {this.myComponent=ref}}
/>
3.2) 컴포넌트 초기 설정
import React, { Component } from "react";
class ScrollBox extends Component {
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;
3.3) 컴포넌트에 메서드 생성
컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드를 생성한다.
- srollTop: 세로 스크롤바 위치(0~350)
- scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
- cilentHeight: 스크롤이 있는 박스의 높이(300)
스크롤바를 맨 아래쪽으로 내리려면 scrollHeight에서 clientHeight 높이를 빼면 된다.
ScrollBox.js
import React, {Component} from 'react';
class ScrollBox extends Component {
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box; // 비구조화 할당 문법 사용
this.box.scrollTop = scrollHeight - clientHeight;
}
render() {
(...)
}
export default ScollBox;
3.4) 컴포넌트에 ref 달고 내부 메서드 사용
App.js
import React, { Component } from "react";
import ScrollBox from "./ScrollBox";
class App extends Component {
render() {
return (
<div>
<ScrollBox ref={(ref) => (this.scrollBox = ref)} />
<button onClick={() => this.scrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
}
export default App;
→ 컴포넌트가 처음 렌더링될 때는 this.scrollBox값이 undefined이므로 this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생하므로 화살표 함수 문법을 사용하여 실행하면 버튼을 누를 때 값을 읽어 와서 실행하므로 오류가 발생하지 않는다.
5.4 정리
- 컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용한다.
- 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용하는 것은 잘못된 사용이다. (컴포넌트끼리 데이터를 교류할 때는 언제나 부모 ↔ 자식 흐름으로 해야 함)
- 함수형 컴포넌트에서는 useRef 라는 Hook 함수를 사용한다. (8장에서 다룸)
'React > [책] 리액트를 다루는 기술' 카테고리의 다른 글
7장. 컴포넌트의 라이프사이클 메서드 (0) | 2021.07.21 |
---|---|
6장. 컴포넌트 반복 (0) | 2021.07.21 |
3장. 컴포넌트(2) (0) | 2021.07.16 |
4장. 이벤트 핸들링 (0) | 2021.07.14 |
3장. 컴포넌트(1) (0) | 2021.07.14 |