안 쓰던 블로그
React-ref (DOM에 이름 달기) 본문
일반 HTML에서 DOM 요소에 이름을 달 때 id를 사용한다
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
기본 생성 프로젝트의 index.js에도 id가 root인 요소에 리액트 컴포넌트를 렌더링하라는 코드가 있음
ReactDOM.render(
<App />, document.getElementById('root')
);
HTML에서 id를 쓴다면 리액트에서는 ref를 쓴다
(리액트에서도 id를 쓸 수는 있지만 중복 id 등의 문제가 있어서 권장하지 않는다)
ref는 DOM을 꼭 직접적으로 건드려야 할 때 사용한다
그냥 state사용
hello-react/src디렉터리 안에 ValidationSample.css 와 ValidationSamplejs 생성
.success{
background-color: lightgreen;
}
.failure{
background-color: lightcoral;
}
.css
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
});
}
handleButtionClick=()=>{
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.handleButtionClick}>검증하기</button>
</div>
);
}
}
export default ValidationSample;
.js
input에서 onChange면 handleChange를 호출해서 state의 password값을 업데이트하기 시작한다
button에서 onClick이 발생하면 clicked값을 true로 바꿨고 valicated값을 설정한다
className에서는 버튼을 누르기 전이면 ''을 리턴,
버튼이 눌렸다면 clicked가 참이면 success값 리턴, 아니면 failure값을 리턴한다
import React, {Component} from 'react';
import ValidationSample from './ValidationSample';
class App extends Component {
render(){
return(
<ValidationSample/>
);
}
}
export default App;
App.js도 수정해준다
참이면 초록색, 거짓이면 빨간색이 표시된다
DOM을 꼭 사용할 수밖에 없는 상황
방금 예제에서는 state로 구현했지만 state를 쓸 수 없는 상황이 있다
-특정 input에 포커스 주기
-스크롤 박스 조작
-Canvase요소 그림 그리기 등
이럴 때는 DOM에 직접 접근해야 하기 때문에 ref를 사용한다
ref사용
ref를 달아야 하는 DOM에 ref속성을 추가한다. 이때는 props를 설정하듯이 하면 된다
ref값으로는 콜백 함수를 전달한다
콜백 함수는 ref를 파라미터로 가지며, 콜백 함수 내의 컴포넌트 멤버 변수에 ref를 담는다
<input ref={(ref)=>{this.input=ref}}></input>
적용
전의 예제에서는 input에다가 값을 넣고 버튼을 클릭하면 깜빡거리는 포커스가 사라졌다
그래서 다시 input을 눌러서 포커스를 해줘야 했다
ValidationSample.js의 일부분을 아래와 같이 수정해본다
handleButtionClick=()=>{
this.setState({
clicked: true,
validated: this.state.password === '0000'
});
this.input.focus();
}
render() {
return (
<div>
<input
type="password"
value={this.state.password}
onChange={this.handleChange}
ref={(ref)=>{this.input=ref}}
className={this.state.clicked ? (this.state.validated ? 'success' : 'failure') : ''}
/>
<button onClick={this.handleButtionClick}>검증하기</button>
</div>
);
}
이제 검증하기 버튼을 눌러도 input박스에서 포커스가 사라지지 않는다(=포커스가 input으로 다시 넘어간다)
컴포넌트에 ref달기
<MyComponent ref={(ref)=>{this.myComponent=ref)} />
이렇게 하면 MyComponent 내부 메서드 및 멤버 변수에도 접근할 수 있따
예를 들어 myComponent.handleClick, myComponent.input 등등
여기서는 스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행해본다
src에 ScrollBox.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: '650px',
background: 'linear-gradient(white, black)'
};
return (
<div
style={style}
ref={(ref)=>{this.box=ref}}>
<div style={innerStyle}/>
</div>
);
}
}
export default ScrollBox;
import React, {Component} from 'react';
import ScrollBox from './ScrollBox';
class App extends Component {
render(){
return(
<div>
<ScrollBox/>
</div>
);
}
}
export default App;
App.js 수정
스크롤이 생겼다
컴포넌트에 메서드 생성
메서드로 스크롤바를 내린다
DOM노드가 가진 값이 있다
-scrollTop: 세로 스크롤바 위치(0~350)
-scrollHeight: 스크롤 박스 내부의 높이(650)
-clientHeight: 스크롤 박스 외부의 높이(300)
스크롤 바를 맨 아래쪽으로 내리려면 scrollHeight-clientHeight를 하면 된다
ScrollBox.js 수정
import React, { Component } from 'react';
class ScrollBox extends Component {
scrollToBottom=()=>{
const {scrollHeight, clientHeight}=this.box;
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: '650px',
background: 'linear-gradient(white, black)'
};
return (
<div
style={style}
ref={(ref)=>{this.box=ref}}>
<div style={innerStyle}/>
</div>
);
}
}
export default ScrollBox;
여기서
const {scrollHeight, clientHeight}=this.box;
부분은 아래와 같은 의미이다
const scrollHeight=this.box.scrollHeight;
const clientHeight=this.box.clientHeight;
App.js 수정
import React, {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>
);
}
}
export default App;
만약 여기서 onClick을 할 때
onClick={this.scrollBox.scrollBottom} 같은 형식으로 했다면 문법 상으로는 틀리지 않았지만
this.scrollBox값이 처음 렌더링 되면 undefined이므로 값을 읽어오면서 오류가 난다
그래서 화살표 함수 문법으로 새로운 함수를 만들어서 scrollBottom메서드를 실행하는 식으로 해서
로딩하면서 렌더링은 한 번 하니까 this.scrollBox를 설정해버리고
버튼을 누를 때에 scrollBottom값을 읽어오는 식으로 해야 오류가 나지 않는다
결과-버튼을 누르면 맨 밑으로 이동한다
정리
-컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용한다
-하지만 ref를 사용하지 않고 구현할 수 있을지 먼저 생각해본다
-예를 들어 서로 다른 컴포넌트끼리 데이터 교류를 할 때 ref를 사용하면 잘못된 것이다(서로 ref를 주고 ref를 또 다른 곳에 주고 주고 주고.. 하다가 코드 구조가 꺼임)
-컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모<->자식 흐름으로 교류해야 한다
-나중에 리덕스를 사용하여 효율적으로 교류하는 방법도 공부하면 좋다
참고한 책-리액트를 다루는 기술
'Web > React' 카테고리의 다른 글
React-컴포넌트 라이프사이클 메서드 (0) | 2020.07.07 |
---|---|
React-컴포넌트 반복(map()과 key) (0) | 2020.07.07 |
React-이벤트 핸들링(onChange, onClick, input 여러 개, onKeyPress) (0) | 2020.07.06 |
React-컴포넌트(컴포넌트 생성, props, state) (0) | 2020.07.06 |
Typo in static class property declaration react/no-typos 에러 해결 방법 (0) | 2020.07.06 |