HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있는데 이것이 ref(reference의 줄임말) 개념이다.
🐳 리액트 컴포넌트 안에서는 id를 사용하면 안 될까?
컴포넌트를 여러 번 사용한다고 가정할 때, HTML에서 DOM의 id는 유일(unique) 해야 하는데, 이런 상황에서는 중복 id를 가진 DOM이 여러 개 생기기 때문에 잘못된 사용이 된다.
ref는 전역적으로 작동하지 않고, 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 생기지 않는다.
-> 'DOM'을 꼭 직접적으로 건드려야 할 때
클래스형 컴포넌트에서 ref를 사용하는 방법을 알아보자!
> ValidationSample.css
.success {
background-color: lightgreen;
}
.failure {
background-color: lightcoral;
}
> ValidationSample.js
import React, { Component } from 'react';
import './ValidationSample.css';
class ValidationSample extends Component {
state = {
password:'',
clicked: false,
validated: false
}
handleChange = (e) => {
this.setState({ //state의 password 값 업데이트
password: e.target.value
});
}
handleButtonClick = () => {
this.setState({
clicked: true, //clicke값 참으로 설정
validated: this.state.password === '0000' //validated 값 검증
})
}
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;
> App.js
import React, { Component } from "react";
import ValidationSample from "./ValidationSample";
class App extends Component {
render() {
return <ValidationSample />;
}
}
export default App;
위의 예제는 state를 사용하여 우리에게 필요한 기능을 구현했지만, state값 만으로는 해결할 수 없는 기능들이 있다.
위와 같은 경우에는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데, 이를 위해 ref를 사용한다.
ref를 사용하는 방법은 2가지이다.
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해 주면 된다.
이 콜백 함수는 ref값을 파라미터로 전달받고,
함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해준다.
<input ref={(ref) => {this.input=ref}} />
import React, { Component } from 'react';
class RefSaple extends Component {
input = React.createRef(); //1
handleFocus = () => {
this.input.current.focus(); //ref를 설정해준 DOM에 접근
}
render() {
return (
<div>
<input ref={this.input} />
</div>
);
}
}
export default RefSample;
첫 번째 방법이었던 콜백 함수를 통한 ref설정을 이용해 앞의 예제에 버튼을 누르면 포커스가 다시 input 쪽으로 자동으로 넘어가는 코드를 작성해보자!
> input에 ref달기
ValidationSample 컴포넌트에 ref를 달아보자
<input
ref={(ref) => this.input=ref}
(...)
/>
> 버튼 onClick 이벤트 코드 수정
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === '0000'
});
this.input.focus();
}
> 완성 코드
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'
});
this.input.focus();
}
render() {
return (
<div>
<input
ref={(ref) => this.input=ref}
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으로 커서가 넘어간다.
<My Component
ref={(ref)=>{this.myComponent=ref}}
/>
이렇게 하면 myComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
(즉, 내부의 ref에도 접근할 수 있다.)
1. ScrollBox 컴포넌트 파일 만들기
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;
2. App 컴포넌트에서 스크롤 박스 컴포넌트 렌더링
import React, { Component } from 'react';
import ScrollBox from './ScrollBox';
class App extends Component {
render() {
return (
<div>
<ScrollBox/>
</div>
);
}
}
export default App;
3. 컴포넌트에 메서드 생성
※ 자바스크립트로 스크롤바를 내릴 때는 DOM 노드가 가진 다음 값들을 사용한다.
import React, { Component } from 'react';
class ScrollBox extends Component {
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
/*
const scrollHeight = this.box.scrollHeight;
const clientHeight = this.box.clientHeight;
*/
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;
4. 컴포넌트에 ref 달고 내부 메서드 사용
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;
※ 주의할 점
문법상으로 onClick = {this.scrollBox.scrollBottom} 같은 형식으로 작성해도 틀린 것은 아니나 컴포넌트가 처음 렌더링 될 때는 this.scrollBox값이 undefined이므로 this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생한다.
화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고 그 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면, 버튼을 누를 때 (이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점) tis.scrollBox.scrollToBottom 값을 읽어 와서 실행하므로 오류가 발생하지 않는다.
[리액트를 다루는 기술]7장 컴포넌트의 라이프사이클 메서드 (0) | 2021.11.08 |
---|---|
[리액트를 다루는 기술]8장 Hooks (0) | 2021.11.08 |
[리액트를 다루는 기술]6장 컴포넌트 반복 (0) | 2021.11.01 |
[리액트를다루는기술]3장 컴포넌트 (0) | 2021.10.11 |
[리액트를다루는기술]4장 이벤트 (0) | 2021.10.11 |