1. ref는 어떤 상황에서 사용해야 할까?
특정 DOM에 작업을 해야 할때 ref를 사용해야함
여기서 특정 작업이란 DOM을 꼭 직접적으로 건드려야 할때를 말함
예를 들어 일반 순수 JS 및 jQuery로 만든 웹사이트에서 input을 검증할때는 특정id를 가진 input에 클래스를 설정함
2. 예제 컴포넌트 생성
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
? "succes"
: "failure"
: ""
}
/>
<button onClick={this.handleButtonClick} 검증하기></button>
</div>
);
}
}
input에서는 onChange 이벤트가 발생하면 handleChange를 호출하여 state의 password 값을 업데이트하게 했다. button에서는 onClick 이벤트가 발생하면 handleButtonClick을 호출하여 clicked 값을 참으로 설정했고, validated 값을 검증 결과로 설정했다.
input의 className 값은 버튼을 누르기 전에는 비어 있는 문자열을 전달하며, 버튼을 누른 후에는검증 결과에 따라 success 값 또는 failure 값을 설정한다. 그리고 이 값에 따라 input 색상이 초록색 또는 빨간색으로 나타남
3. App 컴포넌트에서 예제 컴포넌트 렌더링
import React, { Component } from "react";
import ValidationSample from "./ValidationSample";
class App extends Component {
render() {
return <ValidationSample />;
}
}
export default App;
4. DOM을 꼭 사용해야 하는 상황
state만을 사용하여 필요한 모든 기능을 구현하면 좋겠지만 가끔 state만으로 해결할 수 없는 기능이 있음
1. 특정 input에 포커스 주기
2. 스크롤 박스 조작하기
3. Canvas 요소에 그림 그리기 등
이럴때는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데 이때 바로 ref를 사용함
5. ref 사용
콜백 함수를 통한 ref 설정
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해주면 됨
이 콜백함수는 ref 값을 파라미터로 전달받고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해줌
<input ref={(ref) => {this.input=ref}} />
this.input은 input 요소의 DOM을 가리킴
ref의 이름은 원하는 것으로 자유롭게 지정할 수 있음
DOM 타입과 관계없이 this.superman = ref처럼 마음대로 지정함
createdRef를 통한 ref 설정
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것
이 함수를 사용해서 만들면 더 적은 코드로 쉽게 사용할 수 있음
v16.3부터 도입되어 이전버전에서는 작동하지 않음
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를 넣어 주어야 한다는 것
input에 ref 달기
class ValidationSample extends Component {
state = {
password: "",
clicked: false,
validated: false,
};
handleChange = (e) => {
this.setState({
clicked: true,
validated: this.state.password === '0000'
});
this.input.focus();
};
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
? "succes"
: "failure"
: ""
}
/>
<button onClick={this.handleButtonClick} 검증하기></button>
</div>
);
}
}
6. 컴포넌트에 ref 달기
리액트에서 컴포넌트에도 ref를 달 수 있다. 이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓴다. 컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.
사용법
<MyComponent
ref={(ref) => {this.myComponent=ref}}
/>
MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있음
즉 내부의 ref에도 접근할 수 있음(ex. myComponent.handleClick, myComponent.input 등)
7. 컴포넌트 초기 설정
class ScrollBox extends Component {
render() {
const style = {
border: "1px soild 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>
</div>
);
}
}
8. App 컴포넌트에서 스크롤 박스 컴포넌트 렌더링
import { Component } from 'react';
import ScrollBox from './ScrollBox';
class App extends Componet {
render() {
return (
<div>
<ScrollBox/>
</div>
);
}
}
export fefault App;
9. 컴포넌트에 메서드 생성
JS 스크롤 바 내릴 때는 DOM 노드가 가진 값을 사용함
- scrollTop : 세로 스크롤바 위치(0 ~ 350)
- scrollHeight : 스크롤이 있는 박스 안의 div높이(650)
- clientHeight : 스크롤이 있는 박스의 높이(350)
EX) 스크롤바를 맨 아래쪽으로 내리려면 scrollHeight에서 clientHeight를 빼면 됨
import { Componet } 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() {
(...)
}
export default ScrollBox;
10. 컴포넌트에 ref 달고 내부 메서드 사용
class App extends Component {
render() {
return (
<div>
<ScrollBox ref={(ref) => (this.scrollBox = ref)} />
<button onClick={() => this.scrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
}
문법상으로는 onClick = {this.scrollBox.scrollBottom} 같은 형식으로 작성해도 틀린것은 아님
하지만 컴포넌트가 처음 렌더링될 때는 this.scrollBox 값이 undefined이므로
this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생함
화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고
그 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면 버튼을 누를때(이미 한번 렌더링을 해서 this.scrollBox를 설정한 시점) this.scrollBox.scrollToBottom 값을 읽어 와서 실행하므로 오류가 발생하지 않음
11. 정리
컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용함
먼저 ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 반드시 고려한 후에 활용
컴포넌트끼리 데이터를 교류할때 ref를 사용한다면 이는 잘못된 사용임
할수는 있지만 리액트 사상에 어긋날 설계
컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모, 자식 흐름으로 교류해야함
함수형 컴포넌트에서는 useRef라는 Hook 함수를 사용함
'React > 다시 공부하는 리액트' 카테고리의 다른 글
다시 공부하는 리액트 7장 컴포넌트의 라이프사이클 메서드 (1) | 2023.07.05 |
---|---|
다시 공부하는 리액트 6장 컴포넌트 반복 (0) | 2023.07.04 |
다시 공부하는 리액트 4장 이벤트 핸들링 (0) | 2023.07.03 |
다시 공부하는 리액트 3장 component, state (0) | 2023.07.02 |
7장. 컴포넌트의 라이프사이클 메서드 (0) | 2023.06.22 |