일반 HTML에서 DOM 요소에 이름을 달 때는 id를 사용한다.
요소에 id를 달면 css에서 특정 스타일을 적용하거나 자바스크립트로 해당 요소에 작업을 할 수 있다.
HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법
리액트 컴포넌트에 id를 사용하는 것은 권장하지 않는다.
컴포넌트는 여러 번 사용할 수 있는데 id는 유일한 값이기 때문에 잘못된 사용이 될 수 있다.
ref는 컴포넌트 내부에서만 작동하기 떄문에 문제가 발생하지 않는다.
DOM을 직접 건드려야 할 때 사용한다.
함수 컴포넌트에서 ref를 사용하려면 Hooks를 사용해야한다.
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",
});
console.log(this.state);
};
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;
handleChange
메서드를 통해서 state의 password 값을 업데이트 한다.
handleButtonClick
메서드를 통해서 clicked의 값을 true로 변경하고 validated의 값을 업데이트 한다.
input의 className이 변경됐을 때 css에서 색상을 변경시킨다.
state를 사용하여 필요한 기능을 구현하지만 state만으로 해결할 수 없는 기능이 있다.
이때는 DOM에 직접적으로 접근하기 위해서 ref를 사용한다.
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해주면 된다.
이 콜백 함수는 ref 값을 파라미터로 전달 받으며 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수(this.input)로 설정해준다.
콜백 함수 사용 예시
<input ref={(ref) => {this.input=ref}} />
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.js
handleButtonClick = () => {
this.setState({
clicked: true,
validated: this.state.password === "0000",
});
this.input.focus();
};
<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을 누르게 되면 포커스가 button 요소를 가리키고 있다.
ref를 통해서 button을 눌렀을 때 포커스를 input 요소를 향할 수 있게 한다.
주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 사용한다.
<MyComponent ref={(ref) => {this.myComponent = ref}} />
MyComponent 내부의 메서드 및 멤버 변수에도 접근할 수 있다.
내부의 ref에도 접근 가능 (myComponent.handleClick, myComponent.input 등)
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 (
// 컴포넌트에 ref 달기
<div
style={style}
ref={(ref) => {
this.box = ref;
}}
>
<div style={innerStyle}></div>
</div>
);
}
}
export default ScrollBox;
return 되는 div(ScrollBox Component)에 ref를 달았다.
해당 ref를 통해서 컴포넌트 내부 메서드를 호출할 수 있다.
ScrollBox.js
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
this.box.scrollTop = scrollHeight - clientHeight;
};
ES6의 비구조화 할당 문법을 사용했다.
App.js
import React, { Component } from "react";
import ScrollBox from "./ref/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 컴포넌트 내부를 조작하기 위해서 ScrollBox 컴포넌트에 ref를 적용했다.
scrollToBottom 메서드가 실행되면 this.box의 scrollTop을 업데이트하게 되는데, this.box는 DOM 요소에 적용된 ref를 통해서 해당 요소를 찾을 수 있다.
문법상으로
onClick = {this.scrollBox.scrollBottom}
같은 형식으로 작성해도 틀린 것은 아니다.
하지만 컴포넌트가 처음 렌더링 될 때this.scrollBox
값이undefined
이므로 위의 값을 읽어오는 과정에서 오류가 발생할 수 있다.
화살표 함수를 통해서 새로운 함수를 만들기 때문에 버튼을 클릭했을 때this.scrollBox.scrollBottom
값을 읽어와서 실행하므로 오류가 발생하지 않는다.
컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 <-> 자식 흐름으로 교류해야 한다.
서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용하는 것은 잘못 사용된 것이다. (스파게티처럼 구조가 꼬여서 유지보수가 불가능해진다.)
리덕스 혹은 Context API를 사용해서 효율적으로 교류할 수 있다.
함수 컴포넌트에서 ref를 사용하기 위해서는 useRef라는 Hook 함수를 사용한다.