React

ref : DOM에 이름 달기

ㅇㄱ9 2022. 6. 2. 21:00
728x90

ref란?

: 리액트 프로젝트 내부에서 DOM에 이름을 다는 방법. reference의 줄임말

 

리액트 id를 사용하면 안되나?

: id를 사용할 수는 있으나 JSX안에서 DOM에 id를 달면 해당 컴포넌트를 여러번 사용시 중복 id를 가진 DOM이 여러개 생기므로 사용을 권장하지 않는다. 

반면 ref는 전역적으로 작동하지 않고 컴포넌트 내부에서만 작동하기 때문에 이런 문제가 발생하지 않는다. 

 

ref는 어떤 상황에서 사용해야하는가?

: DOM을 꼭 직접적으로 건드려야 할때 

 

- 특정 input에 포커스 주기 

- 스크롤 박스 조작하기

- Canvas요소에 그림그리기 등

 

ref 사용 

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

: ref를 달고자 하는 요소에 ref라는 콜백 함수를 props로 전달해주면 된다. 이 콜백함수는 ref값을 파라미터로 전달 받고 함수 내부에서 파라미터로 받은 ref를 컴포넌트의 멤버 변수로 설정해 준다. 

//콜백 함수 예시 
<input ref={(ref)=> {this.input=ref}}>

이렇게 하면 this.input은 input요소의 DOM을 가리키게되고 ref의 이름은 원하는것으로 자유롭게 지정할 수 있다. 

ex. this.superman=ref

 

2) createRef를 통한 ref설정

: 리액트 16.3버전 이상에 도입된 createRef라는 함수를 사용하여 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;

컴포넌트 내부에서 멤버 변수로  React.createRef()를 담아주고 해당 멤버 변수를 ref를 달고자 하는 요소에 ref props로 넣어주면 ref설정이 완료된다. 

 

Ref에 접근

render 메서드 안에서 ref가 엘리먼트에게 전달되었을 때, 그 노드를 향한 참조는 ref의 current 어트리뷰트에 담기게 된다.

const node = this.myRef.current;

 

ref의 값은 노드의 유형에 따라 달라진다. 

 

1) DOM엘리먼트에서 Ref 사용하기 

 

ref 어트리뷰트가 HTML 엘리먼트에 쓰였다면, 생성자에서 React.createRef()로 생성된 ref는 자신을 전달받은 DOM 엘리먼트를 current 프로퍼티의 값으로서 받는다.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // textInput DOM 엘리먼트를 저장하기 위한 ref를 생성합니다.
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // DOM API를 사용하여 명시적으로 text 타입의 input 엘리먼트를 포커스합니다.
    // 주의: 우리는 지금 DOM 노드를 얻기 위해 "current" 프로퍼티에 접근하고 있습니다.
    this.textInput.current.focus();
  }

  render() {
    // React에게 우리가 text 타입의 input 엘리먼트를
    // 우리가 생성자에서 생성한 `textInput` ref와 연결하고 싶다고 이야기합니다.
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

컴포넌트가 마운트될 때 React는 current 프로퍼티에 DOM 엘리먼트를 대입하고, 컴포넌트의 마운트가 해제될 때 current 프로퍼티를 다시 null로 돌려 놓는다. ref를 수정하는 작업은 componentDidMount 또는 componentDidUpdate 생명주기 메서드가 호출되기 전에 이루어진다.

 

2) 클래스 컴포넌트에 ref 사용하기 

ref 어트리뷰트가 커스텀 클래스 컴포넌트에 쓰였다면, ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티의 값으로서 받는다.

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} />
    );
  }
}

// 위의 코드는 CustomTextInput가 클래스 컴포넌트 일때만 동작한다. 

class CustomTextInput extends React.Component {
  // ...
}

CustomTextInput 컴포넌트의 인스턴스가 마운트 된 이후에 즉시 클릭되는 걸 흉내내기 위해 CustomTextInput 컴포넌트를 감싸는 걸 원한다면, ref를 사용하여 CustomTextInput 컴포넌트의 인스턴스에 접근하고 직접 focusTextInput 메서드를 호출할 수 있다.

 

3) Ref와 함수 컴포넌트

함수 컴포넌트는 인스턴스가 없기 때문에 함수 컴포넌트에 ref 어트리뷰트를 사용할 수 없다.

function MyFunctionComponent() {
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // 이 코드는 동작하지 않습니다.
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}

함수 컴포넌트에 ref를 사용할 수 있도록 하려면, forwardRef (높은 확률로 useImperativeHandle와 함께)를 사용하거나 클래스 컴포넌트로 변경할 수 있다.

 

다만, DOM 엘리먼트나 클래스 컴포넌트의 인스턴스에 접근하기 위해 ref 어트리뷰트를 함수 컴포넌트에서 사용하는 것은 가능하다. 

function CustomTextInput(props) {
  // textInput은 ref 어트리뷰트를 통해 전달되기 위해서
  // 이곳에서 정의되어야만 합니다.
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

정리

서로 다른 컴포넌트끼리 데이터를 교류할때 ref를 사용하는것을 리액트 사상에 어긋나는 잘못된 사용이다. 

함수형 컴포넌트에서는 useRef라는 Hook을 사용한다. 

 

--------------------------

참고 사이트/ 서적

- 리액트를 다루는 기술 

- 30 Seconds of Knowledge

- Ref와  DOM

 

728x90
반응형