일반 HTML에서 DOM요소에 이름을 달 때는 id를 사용한다.
<div id="my=element></div>
리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다. ref(reference의 줄임말)이란 개념이다.
1. ref는 어떤 상황에서 사용해야 할까?
이것을 사용할 때는 DOM을 직접 건드려야 할 때다. 예를 들면 일반 순수 자바스크립트 및 jQuery로 만든 웹사이트에서 input을 검증할 때는 id를 가진 input에 클래스를 설정해줘야한다. 하지만 리액트에서는 이러한 작업이 필요가 없다. DOM 접근하지 않아도 state로 구현이 가능하다. 이 장에서는 클래스형 컴포넌트에서 ref를 사용하는 방법이다. 함수 컴포넌트에서 ref를 사용하려면 Hooks를 사용해야 하기 때문에 다른 장에서 다루겠다.
리액트 컴포넌트에서 state 사용 순서
VaildationSample 컴포넌트 만들기 -> input에 ref 달기 -> 버튼을 누를 때마다 input에 포커스 주기
2. 예제 컴포넌트 생성
import { 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
import { Component } from 'react';
import ValidationSample from './ValidationSample';
class App extends Component {
render() {
return (
<ValidationSample />
);
}
}
export default App;
3. DOM을 꼭 사용해야 하는 상황
state를 사용하여 필요 기능을 구현하지만, 그렇지 못한 경우도 있다.
1. 특정 input 포커스 주기
2. 스크롤 박스 조작하기
3. Canvas 요소에 그림 그리기
이를 위해서는 ref를 사용한다.
4. ref 사용
콜백함수를 통한 ref 설정
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것이다. ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해주면 된다. 이 콜백 함수는 ref 값을 파라미터로 전달 받는다. 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해준다.
<input ref={(ref) => {this.input=ref}} />
위와 같이 하면 앞으로 this.input은 input 요소의 DOM을 가리킨다. ref의 이름은 원하는 것으로 설정할 수 있다. DOM 타입과 관계없이 this.superman = ref처럼 자유롭게 작명 가능
5. createRef를 통한 ref 설정
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것이다. 이 함수를 사용해서 만들면 더 적은 코드로 사용 가능, 이기능은 v16.3부터 도입되었기 때문에 이전 버젼에서는 작동이 안된다.
import { Component } from 'react';
class RefSample extends Component {
input = React.createRef();
handleFocus = () => {
this.input.current.focus();
}
render(){
return(
<div>
<input ref={this.input} />
</div>
);
}
}
createRef를 사용하여 ref를 만드려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아주어야 한다. 그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면 ref 설정이 완료된다.
설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회하면 된다. 콜백 함수를 사용할 때와 다른점은 어떻게 뒷부분에 .current를 넣어 주어야 한다는 것이다.
6. input에 ref 달기
ref={(ref) => this.input = ref}
7. 버튼 onClick 이벤트 코드 수정
버튼에서 onClick 이벤트가 발생할 때 input에 포커스를 주도록 코드를 수정. 아래 코드는 this.input이 컴포넌트 내부의 Input요소를 가리키고 있어, 일반 DOM을 다루듯 코드 작성이 가능하다.
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === '0000'
});
this.input.focus();
}
8. 컴포넌트에 ref 달기
리액트에서는 컴포넌트에도 ref를 달 수 있다. 이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓰인다. 컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.
사용법
<MyComponent
ref={(ref) => {this.myComponent=ref}}
/>
이렇게 하면 MyComponent 내부의 메서드 및 변수에도 접근이 가능하다. 즉 내부의 ref에도 접근할 수 있다.(예: myComponent.handleClick, myComponent.input 등)
부모컴포넌트에서 스크롤바 내리기
ScrollBox 컴포넌트 만들기 -> 컴포넌트에 ref 달기 -> ref를 이용하여 컴포넌트 내부 메서드 호출하기
9. 스크롤 컴포넌트 파일 생성
import { 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;
import ScrollBox from './ScrollBox';
class App extends Component {
render() {
return (
<div>
<ScrollBox />
</div>
);
}
}
10. 컴포넌트에 메서드 생성
컴포넌트에 스크롤바를 맨 아래쪽 내리는 메서드. 자바스크립트로 스크롤바를 내릴 때는 DOM 노드가 가진 다음 값을 사용한다.
scrollTop : 세로 스크롤바 위치(0~350)
scrollHeight: 스크롤이 있는 박스 안의 div높이(650)
clientHeight: 스크롤이 있는 박스의 높이(300)
스크롤 바를 맨 아래쪽으로 내리려면 scrollHeight에서 clientHeight를 빼면 된다.
import { 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.scrollHeight = 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;
import { 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>
);
}
}
여기서 주의해야 할 점이 있다. 문법상으로는 onClick = {this.scrollBox.scrollToBottom} 같은 형식으로 작성해도 틀린 것은 아니다. 하지만 컴포넌트가 처음 렌더링 될 때는 this.scrollBox 값이 undefined이므로 this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생한다. 화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고, 그 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면, 버튼을 누를 때(이미 한번 렌더링을 해서 this.scrollBox를 설정한 시점) this.scrollBox.scrollToBottom 값을 읽어와서 실행하므로 오류가 발생하지 않는다.
11. 정리
컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용한다. 먼저 ref를 사용하지 않고 원하는 기능을 구현할 수 있는지 확인한다.
그리고 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용한다면 잘못된 것이다. 가능은 하지만
규모가 커질 수록 이렇게 사용한다면 구조가 꼬여서 유지 보수가 불가능하다.
컴포넌트끼리 데이터를 교류할 때는 데이터를 부모 -> 자식 흐름으로 교류해야 한다. 이는
리덕스 혹은 Context API를 사용하여 효율적으로 옮길 수 있다.
아직은 배우진 않았지만, 함수 컴포넌트에서는 useRef라는 Hook 함수를 사용한다. 이는 이후에 배울 것이다.
'React > 리액트(코딩애플)' 카테고리의 다른 글
react-router-dom 사용법 (0) | 2023.06.21 |
---|---|
import / export / state 데이터 바인딩 (0) | 2023.06.21 |
[React] 2023.06.17 (0) | 2023.06.17 |
[React] 2023.06.13 2일차 (0) | 2023.06.14 |
리액트 설치 및 레이아웃 JSX문법 / state 문법 (0) | 2023.06.13 |