개발새발개발/React

React - ref

birdsfoot 2024. 12. 29.

Preview

  • HTML에서 id 를 사용해 이름을 다는 것처럼 React에서는 ref(reference)를 이용해 DOM 에 이름을 붙여준다
  • id를 사용할 경우 컴포넌트가 반복될 때, 고유해야할 id 값이 여러번 사용될 수 있으므로 ref를 사용하는 걸 권장한다
  • ref는 컴포넌트 내부에서만 작동한다

 

ref

  • DOM을 직접적으로 건드려야 할 때 사용한다
  • 비밀번호 검증 페이지 만들기 실습
    1. ValidationSample 컴포넌트 만들기
    2. input에 ref 달기
    3. 버튼을 누를 때마다 input에 포커스 주기

 

1. 예제 컴포넌트 생성

  • input에서 onChange 이벤트가 발생하면 handleChange 호출하여 state의 password 값을 업데이트
  • button에서 onClick 이벤트가 발생하면 handleButtonClick 을 호출하여 clicked 값을 true로 설정, validated 검증
  • className 값은 버튼을 누르기 전에는 비어있는 문자열 전달, 버튼을 누른 후에는 검증 결과에 따라 sucess 또는 faulure 반환, 결과에 따라 색상 변경
  • 이전에 배운 onKeyDown 이벤트 핸들링 추가
// 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',
    });
  };

  handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      this.handleButtonClick();
    }
  };
  render() {
    return (
      <div>
        <input
          type="password"
          value={this.state.password}
          onChange={this.handleChange}
          className={
            this.state.clicked
              ? this.state.validated
                ? 'sucess'
                : 'failure'
              : ''
          }
          onKeyDown={this.handleKeyPress}
        />
        <button onClick={this.handleButtonClick}>검증하기</button>
      </div>
    );
  }
}

export default ValidationSample;

 

/* ValidationSample.css */

.sucess {
  background-color: lightgreen;
}

.failure {
  background-color: lightcoral;
}

 

2. App 컴포넌트에 렌더링

//App.js

import React, { Component } from 'react';
import ValidationSample from './ValidationSample';

class App extends Component {
  render() {
    return (
      <div>
        <ValidationSample />
      </div>
    );
  }
}

export default App;

 

3. 결과

 

 

4. DOM을 꼭 사용해야 하는 상황

  • state만으로는 해결하기 어려운 기능이 있을 땐 ref를 사용한다
    • 특정 input에 포커스 주기
    • 스크롤 박스 조작하기
    • Canvas 요소에 그림 그리기 등

 

 

ref 사용하기

1. 콜백 함수를 통한 ref 설정

  • ref를 달고자 하는 요소에 ref 라는 콜백 함수를 props 해주기
  • 콜백 함수는 ref 값을 파라미터로 전달 받고, 함수 내부에서 컴포넌트의 멤버 변수로 설정
<input ref = {(ref) => {this.input=ref}} />
  • 이렇게 하면 this.input은 input 요소의 DOM을 가리킴

 

2. createRef를 통한 ref 설정

  • 리액트 내장함수 createRef 사용
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;
  • 사용을 위해선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 먼저 담아줘야 함
  • 해당 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면 설정 완료
  • 설정한 뒤 ref를 설정해 준 DOM에 접근하려면 this.input.current로 조회

 

3. 검증하기를 누른 후에도 커서가 input에 있도록 설정하기

3-1. input에 ref 달기

(...)
    <input
      ref={(ref) => (this.input = ref)}
	(...)
    />

 

 

3-2. 버튼 onClick 이벤트 코드 수정

handleButtonClick = () => {
this.setState({
  clicked: true,
  validated: this.state.password === '0000',
});
this.input.focus();
};

 


 

컴포넌트에 ref 사용하기

  • 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 쓸 때 사용
  • DOM에 ref 다는 방법과 같음

 

사용법

<MyComponent
	ref = {(ref) => {this.myComponent=ref}}
/>
  • 이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에 접근 가능
  • 즉 내부의 ref에 접근 가능(myComponent.handleClick, myComponent.input)

 

스크롤 박스 실습

진행 순서

  1. ScrollBox 컴포넌트 만들기
  2. 컴포넌트에 ref 달기
  3. ref를 이용하여 컴포넌트 내부 메서드 호출하기

 

1. 컴포넌트 초기 설정

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;
  • App에도 등록하기
  • 결과

 

2. 컴포넌트에 메서드 생성

  • 컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드 생성
    • scrollTop : 세로 스크롤바 위치(0~350)
    • scrollHeight : 스크롤이 있는 박스 안의 div 높이(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() {
(...)
}

export default ScrollBox;

 

 

 

3. 컴포넌트에 ref 달고 내부 메서드 사용

//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={this.scrollBox.scrollToBottom}`으로 적으면 this.scrollBox 값이 undefined이므로 오류가 발생할 수 있음
  • 화살표 함수를 만들면 이미 한번 렌더링되면서 this.scrollBox가 설정되므로, 버튼을 누를 때 오류가 생기지 않음
  • "첫페이지 렌더링 + 버튼 클릭" 을 "첫페이지 렌더링 -> 버튼 클릭" 으로 동작하게 하는 것

 

 

ref 사용 주의사항

  • 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref 를 사용하면 나중에 꼬여서 유지보수가 어려워짐
  • 부모 - 자식 흐름으로 교류해야 함

 

댓글