본문 바로가기
IT/스터디

Ref

by 봉즙 2020. 1. 29.

Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공

 

일반적인 React의 데이터 플로우에서 props를 부모 컴포넌트가 자식과 상호 작용하는 유일한 수단이다. 자식을 수정하기 위해서는 새로운 props를 전달하여 자식을 다시 렌더링해야 한다. 그러나, 직접적으로 수정해야 하는 경우도 존재한다.

Ref 사용 시기

  • 포커스, 텍스트 선택영역, 미디어의 재생을 관리할 때
  • 애니메이션 직접 실행시
  • 서드 파티 DOM 라이브러리를 React와 같이 사용할 때

※남발하지 않도록 하는 것이 좋다.

 

Ref 생성

ref는 React.createRef()를 통해서 생성되며 ref attribute를 통해 React 엘리먼트에 부착된다. 컴포넌트의 인스턴스가 생성될 때 Ref를 프로퍼티로서 추가하며, 컴포넌트의 인스턴스의 어느곳에서도 ref에 접근 할 수 있도록 한다.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

 

Ref 접근

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

const node = this.myRef.current;

ref의 값은 노드 유형에 따라 다르다

  • ref attribute가 HTML 엘리먼트에서 쓰인경우 생성자에서 React.createRef()로 생성된 ref는 자신을 전달받은 DOM 엘리먼트를 current 프로퍼티의 값으로서 받는다.
  • ref attribute가 커스텀 클래스 컴포넌트에 스였다면, ref 객체는 마운트된 컴포넌트의 인스턴스를 current 프로퍼티의 값으로서 받는다.
  • 함수컴포넌트는 인스턴스가 없기에  함수 컴포넌트에 ref attribute를 사용할 수 없다.

 

DOM 엘리먼트에 Ref 사용 (DOM노드에 대한 참조)

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 생명주기 메서드가 호출되기 전에 이루어진다.

 

클래스 컴포넌트에 ref 사용

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 컴포넌트의 인스턴스가 마운트된 이후 즉시 클릴되는 걸 흉내내기 위해 ref를 사용하여 인스턴스에 접근하고 직접 focusTextInput 메서드를 호출 할 수 있다.

 

Ref 함수 컴포넌트

함수 컴포넌트는 인스턴스가 없기에 ref attribute를 사용할 수 없다.

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

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

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

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

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

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

 

부모 컴포넌트에게 DOM ref 전달

ref forwarding을 사용하는 것이 권장된다. Ref 전달하기는 컴포넌트가 자식 컴포넌트의 ref를 자신의 ref로서 외부에 노출시키게 한다.

 

콜백 ref

ref attribute에 React.createRef()를 통해 생성된 ref를 전달하는 대신 함수를 전달한다. 전달된 함수는 다른 곳에 저장되고 접근 될 수 있는 React 컴포넌트의 인스턴스나 DOM 엘리먼트를 인자로 받는다.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // DOM API를 사용하여 text 타입의 input 엘리먼트를 포커스합니다.
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // 마운트 되었을 때 자동으로 text 타입의 input 엘리먼트를 포커스합니다.
    this.focusTextInput();
  }

  render() {
    // text 타입의 input 엘리먼트의 참조를 인스턴스의 프로퍼티
    // (예를 들어`this.textInput`)에 저장하기 위해 `ref` 콜백을 사용합니다.
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

 

ref 콜백이 인라인 함루로 선언된다면 처음엔 null로 다음엔 DOM 엘리먼트로 총 두번 호출된다. 매 렌더링마다 ref 콜백의 새인스턴스가 생성되어 ref를 제거하고 새 ref를 설정해야 하기 때문이다. ref 콜백을 클래스에 바인딩된 메서드로 선언해서 해결할 수 있다.

 

'IT > 스터디' 카테고리의 다른 글

Hook  (0) 2020.02.04
list와 key  (0) 2020.01.30
이벤트 핸들링  (0) 2020.01.23
클래스형 컴포넌트, state, props  (0) 2020.01.22
JSX  (0) 2020.01.21

댓글