리액트를 다루는 기술 5장

riverkim·2022년 6월 1일
0
post-thumbnail

이 글은 책 리액트를 다루는 기술을 개인적으로 정리한 글 입니다.

ref:DOM에 이름 달기

HTML에서 id를 사용하여 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 ref(reference의 줄임말) 사용

ref는 어떤 상황에서 사용해야 할까?

특정 DOM에 작업을 해야 할 때 ref를 사용 -> DOM을 꼭 직접적으로 건드려야 할 때

DOM을 꼭 사용해야 하는 상황

state만으로 해결할 수 없는 기능

• 특정 input에 포커스 주기
• 스크롤 박스 조작하기
• Canvas 요소에 그림 그리기 등

이때는 어쩔 수 없이 DOM에 직접적으로 접근해야 하는데, 이를 위해 바로 ref를 사용

ref 사용

ref를 사용하는 방법은 두 가지

  • 콜백 함수를 통한 ref 설정
  • createRef를 통한 ref 설정

콜백 함수를 통한 ref 설정

ref를 만드는 가장 기본적인 방법 -> 콜백 함수 사용
ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달
이 콜백 함수는 ref 값을 파라미터로 전달받음 그리고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정

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

이렇게 하면 앞으로 this.input은 input 요소의 DOM을 가리킴
ref의 이름은 원하는 것으로 자유롭게 지정할 수 있음
DOM 타입과 관계없이 this.superman = ref처럼 마음대로 지정

createRef를 통한 ref 설정

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;

createRef를 사용하여 ref를 만들려면 우선 컴포넌트 내부에서 멤버 변수로 React.createRef()를 담아 줌
그리고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어 주면 ref 설정이 완료

설정한 뒤 나중에 ref를 설정해 준 DOM에 접근하려면 this.input.current를 조회
콜백 함수를 사용할 때와 다른 점은 이렇게 뒷부분에 .current를 넣어 주어야 한다는 것

컴포넌트에 ref 달기

리액트에서는 컴포넌트에도 ref를 달 수 있음
이 방법은 주로 컴포넌트 내부에 있는 DOM을 컴포넌트 외부에서 사용할 때 씀

// DOM에 ref를 다는 방법과 똑같음
<MyComponent
  ref={(ref) => {this.myComponent=ref}}
/>

이렇게 하면 MyComponent 내부의 메서드 및 멤버 변수에도 접근 가능 -> 즉, 내부의 ref에도 접근할 수 있음
(예: myComponent.handleClick, myComponent.input 등)

// 스크롤 박스가 있는 컴포넌트를 하나 만들고, 스크롤바를 아래로 내리는 작업을 부모 컴포넌트에서 실행
// 최상위 DOM에 ref를 달기
import React, { Component } from 'react';
class ScrollBox extends Component {
  // 컴포넌트에 스크롤바를 맨 아래쪽으로 내리는 메서드
  // 이렇게 만든 메서드는 부모 컴포넌트인 App 컴포넌트에서 ScrollBox에 ref를 달면 사용가능
  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: '650px',
      background: 'linear-gradient(white, black)'
    };
    return (
      
      <div
        style={style}
        ref={ref => {
          this.box = ref;
        }}
      >
        <div style={innerStyle} />
      </div>
    );
  }
}
export default ScrollBox;

자바스크립트로 스크롤바를 내릴 때는 DOM 노드가 가진 다음 값들을 사용

• scrollTop: 세로 스크롤바 위치(0~350)
• scrollHeight: 스크롤이 있는 박스 안의 div 높이(650)
• clientHeight: 스크롤이 있는 박스의 높이(300)

scrollHeight에서 clientHeight 높이를 빼면 -> 스크롤바를 맨 아래쪽 이동

주의할 점
문법상으로는 onClick = {this.scrollBox.scrollBottom} 같은 형식으로 작성해도 틀린 것은 아님
하지만 컴포넌트가 처음 렌더링될 때는 this.scrollBox 값이 undefined이므로
this.scrollBox.scrollToBottom 값을 읽어 오는 과정에서 오류가 발생

화살표 함수 문법을 사용하여 아예 새로운 함수를 만들고 그 내부에서 this.scrollBox.scrollToBottom 메서드를 실행하면 버튼을 누를 때
(이미 한 번 렌더링을 해서 this.scrollBox를 설정한 시점) this.scrollBox.scrollToBottom 값을 읽어 와서 실행(오류발생 X)

정리

컴포넌트 내부에서 DOM에 직접 접근해야 할 때는 ref를 사용
ref를 사용하지 않고도 원하는 기능을 구현할 수 있는지 반드시 고려한 후에 활용해야 함

이 시점에서 오해할 수 있는 부분이 있는데, 서로 다른 컴포넌트끼리 데이터를 교류할 때 ref를 사용한다면 이는 잘못 사용된 것
컴포넌트끼리 데이터를 교류할 때는 언제나 데이터를 부모 ↔ 자식 흐름으로 교류

profile
Hello!

0개의 댓글