[React] ref로 DOM에 이름달기
ref -> DOM에 id부여하기\
일반적인 HTML에서는 DOM요소에 이름을 달 때, id를 부여한다.
리액트 프로젝트 내부에서 id를 부여하기 위해서는 ref를 사용해야한다.
id를 직접부여하지 않는 이유
리액트 프로젝트에서 DOM에 if를 부여할때 리턴되는 HTML객체(JSX로 표현한 태그들)에 ID를 직접 부여하지 않는 이유는, 리액트 컴포넌트는 재사용
하기 때문이다.
특정 컴포넌트 A가 있을때, 그 컴포넌트를 여러번 사용할 때, id값을 부여하면 컴포넌트가 사용되는 회수만큼 id
가 중복될 것이다.
공식문서에서 ref
Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공합니다.
일반적인 React의 데이터 플로우에서 props는 부모 컴포넌트가 자식과 상호작용할 수 있는 유일한 수단입니다. 자식을 수정하려면 새로운 props를 전달하여 자식을 다시 렌더링해야 합니다. 그러나, 일반적인 데이터 플로우에서 벗어나 직접적으로 자식을 수정해야 하는 경우도 가끔씩 있습니다. 수정할 자식은 React 컴포넌트의 인스턴스일 수도 있고, DOM 엘리먼트일 수도 있습니다. React는 두 경우 모두를 위한 해결책을 제공합니다.
ref를 사용하는 상황
DOM
을 직접 건드려야할때, 사용한다.
간단한 비밀번호 검증 구현
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 = (e) => {
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;
input 태그는 내용이 입력됨에따라 onChange함수로 state를 변경하면서 변경된 state로 value값을 유지한다.
button 태그에서 버튼이 클릭되면 setState를 통해 clicked state를 true로 변환하고, validated state는 input 태그에서 value값으로 가지고있는 password state에따라 true나 false가 리턴된다.
input 태그의 클래스를 부여할때 삼항 연산자가 2번쓰였는데, clicked가 false이면 빈 문자열을,
true이면 현재 validated가 true인지 false인지에 따라 다른 클래스를 갖게한다.
DOM을 사용해야하는 상황
앞 예제를 보면, 비밀번호 입력 후, 자동으로 focus()가 해제된다.
사용자입장에서 굉장히 불편할텐데, 이를 해결하기 위해서는 비밀번호를 입력하고 맞으면 다음페이지로 넘어가고(구현은 다음에..) 틀리면 비밀번호를 입력하는 칸에 다시 focus가 가야한다.
이때, DOM을 직접 사용해야하는데, 특정 DOM객체에 포커스를 주기 위해서는 직접적으로 접근해야한다.
이때 ref를 사용한다.
ref 사용
ref를 사용하는 방법에는 콜백함수를 이용하거나, 리액트 내장함수를 사용하는 방법이 있다.
콜백함수
<input
ref={(ref) => {
this.input = ref;
}}
type="password"
value={this.state.password}
onChange={this.handleChange}
className={
this.state.clicked ? (this.state.validated ? "success" : "failure") : ""
}
></input>
- ref를 달고자하는 요소에 props로 ref를 만든다.
- 이 ref는 콜백함수를 props로 받는다.
- 이 콜백함수는 파라미터로 ref를 받고, 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버변수취급한다.
handleButtonClick = (e) => {
this.setState({
clicked: true,
validated: this.state.password === "0000",
});
console.log(this.input);
};
검증하기 버튼의 이벤트리스너 콜백함수에 console.log(this.input)
을 하면
이렇게 DOM객체를 리턴한다
이제 이 컴포넌트에서 this.input은 input요소의 DOM을 가리킨다는 것을 알 수 있다.
내장함수
16.3 버전부터 내장함수 createRef를 통해 만들 수 있게 되었다.
import React, { Component } from "react";
class RefSample extends Component {
input = React.createRef();
handleFocus = () => {
this.input.current.focus();
};
handleOnClick = () => {
console.log(this.input.current);
};
render() {
return (
<div>
<input ref={this.input}></input>
<button onClick={this.handleOnClick}>inputdom확인</button>
</div>
);
}
}
export default RefSample;
컴포넌트 내부에서 멤버변수로 React.createRef()함수를 담아주었다.
긜고 이 멤버변수를 ref를 달고자하는 엘리먼트에 props로 전달한다.
나중에 ref를 설정해준 DOM에 접근하기 위해서는 this.input.current
를 조회한다.
그냥 this.input으로 접근하면, current를 키값으로하는 객체가 리턴되기 때문이다.
ref 적용
- input에 ref달기
<input
ref={(ref) => {
this.input = ref;
}}
type="password"
value={this.state.password}
onChange={this.handleChange}
className={
this.state.clicked ? (this.state.validated ? "success" : "failure") : ""
}
></input>
- 검증하기 버튼을 클릭하면 포커스가 input에 가도록만들기
handleButtonClick = (e) => {
this.setState({
clicked: true,
validated: this.state.password === "0000",
});
this.input.focus();
};
컴포넌트에 ref 달기
컴포넌트 내부 DOM을 컴포넌트 외부에서 사용할때 쓴다.
컴포넌트에 ref다는 법은 DOM에 ref다는 법과 같다.
DOM을 직접적으로 조작하는 경우 중 스크롤을 조작해야할 때에 대해서 공부한다.
컴포넌트에 ref 다는법
<MyComponent
ref={(ref) => {
this.myComponent = ref;
}}
/>
이렇게 설정하면 컴포넌트 내부의 메서드
와 멤버 변수
에도 접근할 수 있다.
이번 예제는 스크롤 박스가 있는 컴포넌트를 하나 만들고, 스크롤바를 아래로 내리는 버튼을 부모컴포넌트에 달아주는 것이다.
컴포넌트 초기설정
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 (
<div
style={style}
ref={(ref) => {
this.box = ref;
}}
>
<div style={innerStyle} />
</div>
);
}
}
export default ScrollBox;
컴포넌트에 메서드 생성
아래 함수를 ScrollBox 컴포넌트 내부에서 정의한다
scrollToBottom = () => {
const { scrollHeight, clientHeight } = this.box;
this.box.scrollTop = scrollHeight - clientHeight;
};
바깥 div 안에 gradient가 먹여져있는 650px높이의 div가 있다.
이 div를 this.box ref를 가진 div가 감싸고있는데,
이 DOM은 scrollTop, scrollHeight, clientHeight와 같은 값을 가진다.
scrollTop : 세로 스크롤바의 위치
scrollHeight : 스크롤이 있는 박스안의 div높이,(안쪽 div, 650px)
clientHeight : 스크롤이 있는 박스(바깥 div)의 높이(300px)
스크롤이 내려갈때마다 overflow된 내용이 바깥 div안쪽으로 들어와 표현된다.
스크롤이 0일때 안쪽 div의 최상단에 위치한다.
따라서 맨 아래로 스크롤을 내리기 위해서는 스크롤이 overflow되어 안보이는 만큼,(350)
내려와야한다.
컴포넌트에 ref달고 내부 메서드 사용
import ScrollBox from "./ScrollBox";
import React, { Component } from "react";
class App extends Component {
render() {
return (
<div>
<ScrollBox ref={(ref) => (this.ScrollBox = ref)} />
<button onClick={() => this.ScrollBox.scrollToBottom()}>
맨 밑으로
</button>
</div>
);
}
}
export default App;
부모컴포넌트에서 ScrollBox 컴포넌트에 ref로 this.ScrollBox를 달아주었다.
이제 App컴포넌트에서 ScrollBox컴포넌트의 DOM에 직접 접근할 수 있는데,
ScrollBox컴포넌트에서 갖고있는 scrollToBottom메서드를 onClick 속성으로 가지는 button을 만들면 된다.
댓글남기기