GitHub - gilbutITbook/080203: 리액트를 다루는 기술 개정판
리액트를 다루는 기술 개정판. Contribute to gilbutITbook/080203 development by creating an account on GitHub.
github.com
(본 포스팅은 위의 리액트를 다루는 기술(개정판)을 기반으로 작성했습니다.)
- 일반 HTML에서 DOM 요소에 이름을 달 때 id를 사용
<div id="my-element"></div>
- HTML에서 id를 사용하는 것처럼, 리액트 내부에서 DOM에 이름을 다는 방법이 있는데 그것이 ref(reference) 개념
- 리액트 컴포넌트 안에서도 id를 사용할 수 있지만, HTML에서 DOM의 id는 유일해야 하는데 컴포넌트가 여러 번 사용되면 중복 id가 생기니 잘못된 사용
- ref는 전역적으로 작동하지 않고, 컴포넌트 내부에서만 작동하기 때문에 문제가 발생하지 않는다.
'ref는 어떤 상황에서 사용해야 할까?'
- 특정 DOM을 꼭 직접적으로 건드려야 할 때
- DOM을 꼭 사용해야 하는 상황
- 특정 input에 포커스 주기
- 스크롤 박스 조작하기
- Canvas 요소에 그림 그리기 ...
특정 DOM을 꼭 직접적으로 건드려야 할 때
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Example</title>
<style>
.success {
background-color: lightgreen;
}
.failure {
background-color: lightcoral;
}
</style>
<script>
function validate() {
var input = document.getElementById('password');
input.className='';
if(input.value==='0000') {
input.className='success';
} else {
input.className='failure';
}
}
</script>
</head>
<body>
<input type="password" id="password"></input>
<button onclick="validate()">Validate</button>
</body>
</html>
DOM에 접근하지 않고 state로 구현할 때
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({
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}>Validate</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;
'ref 사용' - 콜백 함수를 통한 ref 설정
- 콜백 함수 사용 ← ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달. 이 콜백 함수는 ref 값을 파라미터로 전달 받아 컴포넌트의 멤버 변수로 설정
- this.input은 input 요소의 DOM을 가리킴
<input
ref={(ref) => {
this.input = ref;
}}
/>
'ref 사용' - CreateRef를 통한 ref 설정
- 리액트에 내장되어 있는 createRef 함수를 사용해 ref를 만들 수도 있음
- 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아줌
- 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면 ref 설정 완료
- ref를 설정해준 DOM에 접근하려면 this.input.current를 조회
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;
'ValidationSample에 ref 적용하기'
input에 ref 달기
<input
ref={(ref) => this.input=ref}
(…)
/>
- ValidationSample.js - input 요소
- input에 ref 적용으로 this.input은 input을 가르킴
버튼 onClick 이벤트 코드 수정
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === '0000'
});
this.input.focus();
}
- ValidationSample.js - handleButtonClick 메서드
- input에 ref로 적용시켜서 this.input이 동작
'컴포넌트에 ref 달기'
- DOM과 방법 동일하게 component에도 ref를 달 수 있음
- 내부의 ref(메서드 및 멤버 변수)에도 접근할 수 있게 됨
<MyComponent
ref={ref => {
this.myComponent = ref;
}}
/>
→ 이렇게 한 이후 myComponent.handleClick, myComponent.input등 컴포넌트 내부 ref(DOM 요소)에 접근할 수 있음
ex) myComponent.handleClick, myComponent.input
//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;
//ScrollBox.js
import React, {Component} from "react";
class ScrollBox extends Component {
scrollToBottom = () => {
const {scrollHeight, clientHeight} = this.box;
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: '500px',
background: 'linear-gradient(white, black)'
}
return (
<div style={style} ref={(ref) => {
this.box = ref
}}>
<div style={innerStyle}/>
</div>
);
}
}
export default ScrollBox;
scrollTop: 세로 스크롤바 위치(0~350)
scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
clientHeight: 스크롤이 있는 박스의 높이(300)
후기 및 정리
- 컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용, 하지만 ref를 사용하지 않고 원하는 기능을 구현할 수 있는지를 반드시 고려해야 한다.
- 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용하는 것이라고 오해할 수 있는데, 그렇게 하는 것은 애플리케이션의 구조를 꼬이게 만들 수 있는 위험한 방법이다. 따라서 데이터를 교류할 때는 언제나 부모-자식 흐름으로 교류하는 것이 좋다.
- 함수형 컴포넌트에서는 useRef라는 Hook 함수를 사용한다.
'React' 카테고리의 다른 글
7# 컴포넌트의 라이프사이클 메서드 (0) | 2023.02.03 |
---|---|
6# 컴포넌트 반복 (0) | 2023.01.27 |
4# 이벤트 핸들링 (0) | 2023.01.05 |
3# 컴포넌트 (0) | 2022.12.30 |
2# JSX (0) | 2022.12.23 |