- HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법이 있다. → ref(reference)
리액트 컴포넌트 안에서 id를 사용 못하는가?
사용할 수는 있지만, 같은 컴포넌트를 여러 번 사용한다고 가성하면, HTML에서 DOM의 id는 유일해야하는데, 이런 상황에서는 중복 id를 가진 DOM이 여러 개 생긴다. → 잘못된 사용! ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 생기지 않는다.
특정 DOM에 작업을 해야 할 때 ref를 사용한다. → 구체적으로 DOM을 꼭 직접적으로 건드려야 할 때 사용한다.
/* ValidationSample.css */
.success {
background-color: lightgreen;
}
.failure {
background-color: lightcoral;
}
//ValidationSample.js
import React, { Component } from "react";
import "../css/ValidationSample.css"; //css를 import한다.
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"
: ""
}
></input>
<button onClick={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
state를 사용하여 우리에게 필요한 기능을 구현했지만, 가끔 state만으로 해결할 수 없는 기능이 있다.
이때는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데, 이를 위해 바로 ref를 사용한다.
ref 사용하는 방법은 두 가지이다.
ref를 만드는 가장 기본적인 방법은 콜백 함수를 사용하는 것이다. ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해준다. 이 콜백 함수는 ref 값을 파라미터로 전달받는다. 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정한다.
<input ref = {(ref) => {this.input = ref}} />
이렇게 하면 앞으로 this.input은 input 요소의 DOM을 가리킨다. ref의 이름은 원하는 것으로 자유롭게 지정할 수 있다. DOM 타입과 관계없이 this.superman = ref
처럼 마음대로 지정한다.
ref를 만드는 또 다른 방법은 리액트에 내장되어 있는 createRef라는 함수를 사용하는 것이다.
//RefSample.js
import React, { Component } from "react";
class RefSample extends Component {
input = React.createRef(); // 1. createRef 함수 사용, 멤버 변수로 React.crateRef()를 담아 줌
handleFocus = () => {
this.input.current.focus(); // 3. ref를 설정해 준 DOM에 접근
};
render() {
return (
<div>
<input ref={this.input}></input> // 2. 멤버 변수를 ref props로 넣어 줌
</div>
);
}
}
export default RefSample;
ValidationSample 컴포넌트는
import React, { Component } from "react";
import "../css/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(); // input에 포커스를 준다
};
render() {
return (
<div>
<input
ref={(ref) => (this.input = ref)} // 콜백 함수를 사용하여 ref 설정
type="password"
value={this.state.password}
onChange={this.handleChange}
className={
this.state.clicked
? this.state.validated
? "success"
: "failure"
: ""
}
></input>
<button onClick={this.handleButtonClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
리액트에서는 컴포넌트에도 ref를 달 수 있다. 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 쓴다. 컴포넌트에 ref를 다는 방법은 DOM에 ref를 다는 방법과 같다.
<MyComponent
ref={(ref) => {
this.myComponent = ref;
}}
/>;
MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다. 즉, 내부의 ref에도 접근할 수 있다. (예 : myComponent, handleClick, myComponent, input 등)
스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행.
스크롤 박스가 있는 컴포넌트를 하나 만들고 → 컴포넌트에 ref 달고 → ref를 이용하여 컴포넌트 내부 메서드 호출
//ScollBox.js
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: "650%",
background: "linear-gradient(white, black)",
};
return (
<div
style={style}
ref={(ref) => {
this.box = ref;
}}
>
<div style={innerStyle} />
</div>
);
}
}
export default ScrollBox;
컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드를 만들자. 자바스크립트로 스크롤바를 내릴 때는 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.cliengHeight;
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: "650%",
background: "linear-gradient(white, black)",
};
return (
<div
style={style}
ref={(ref) => {
this.box = ref;
}}
>
<div style={innerStyle} />
</div>
);
}
}
export default ScrollBox;
스크롤바를 맨 아래쪽으로 내리려먼 scrollHeight
에서 clientHeight
높이를 빼면 된다.
App 컴포넌트에서 ScrollBox에 ref를 달고 버튼을 만들어 누르면, ScrollBox 컴포넌트의 scrollToBottom 메서드를 실행하도록 코드를 만든다.
//App.js
import React, { Component } from "react";
import "./App.css";
import ScrollBox from "./components/ScrollBox";
class App extends Component {
render() {
return (
<div>
<ScrollBox ref={(ref) => (this.scrollBox = ref)}></ScrollBox>
<button onClick={() => this.scrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
}
export default App;
App.js 코드는 함수형 컴포넌트로 바꾸면 에러가 난다. 이유를 찾아보니 함수형 컴포넌트에서는 ref를 제한적으로 사용할 수 있다. 아래의 공식문서 확인하기.
https://reactjs-kr.firebaseapp.com/docs/refs-and-the-dom.html
useRef
라는 Hook 함수를 사용한다. React.createRef
와 유사하다.