React에서 Refs 활용하기

Refs는 DOM 노드나 React 인스턴스에 접근할 수 있는 방법을 제공합니다. 아래는 Refs를 사용하는 몇 가지 시나리오입니다.

  • 초점 관리, 텍스트 선택 또는 미디어 재생
  • 강제 애니메이션 트리거
  • 제3자 DOM 라이브러리 통합

1. Refs 생성 및 접근

  • Refs 생성 Refs는 React.createRef()로 생성되며 ref 속성을 통해 React 요소에 연결됩니다. 일반적으로 컴포넌트를 생성할 때 Refs를 인스턴스 속성에 할당하여 전체 컴포넌트에서 이를 참조할 수 있습니다.
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
  • Refs 접근 render 메서드의 요소에 ref가 전달되면 해당 노드에 대한 참조는 ref 객체의 current 속성을 통해 접근 가능합니다.
const node = this.myRef.current;

ref 값은 노드 유형에 따라 다릅니다: HTML 요소에 ref 속성이 사용되면 React.createRef()로 생성된 ref는 그 current 속성으로 기본 DOM 요소를 받습니다. 사용자 정의 클래스 컴포넌트에 ref 속성이 사용되면 ref 객체는 마운트된 인스턴스를 current 속성으로 받습니다. 함수 컴포넌트에는 ref 속성을 사용할 수 없습니다. 함수 컴포넌트에는 인스턴스가 없기 때문입니다.

DOM 요소에 Ref 추가

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

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

  render() {
    return (
      <div>
        <input type="text" ref={this.textInput} />
        <button onClick={this.focusTextInput}>텍스트 입력 초점 맞추기</button>
      </div>
    );
  }
}

클래스 컴포넌트에 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} />;
  }
}

함수 컴포넌트에서 Ref 사용 함수 컴포넌트는 인스턴스가 없으므로 직접 ref를 사용할 수 없습니다. 대신 React.forwardRef()를 사용하여 Ref를 전달할 수 있습니다.

function CustomTextInput({ onButtonClick }) {
  const textInput = React.useRef(null);

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

  return (
    <div>
      <input type="text" ref={textInput} />
      <button onClick={handleClick}>텍스트 입력 초점 맞추기</button>
    </div>
  );
}

2. Ref 전달

Ref 전달은 ref를 자동으로 하위 컴포넌트로 전달하는 기술입니다. 이는 특정 컴포넌트가 ref를 받고 이를 하위 컴포넌트로 전달(즉 "전달")하는 옵션 기능입니다.

아래 예시에서는 FancyButton 컴포넌트를 통해 하위 DOM 버튼 노드의 ref를 얻을 수 있습니다.

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

const buttonRef = React.createRef();
<FancyButton ref={buttonRef}>클릭하세요!</FancyButton>;
  • React.createRef()로 ref를 생성하고 변수에 할당합니다.
  • JSX 속성으로 ref를 지정하여 <FancyButton ref={ref}>로 전달합니다.
  • React는 forwardRef 내부 함수의 두 번째 매개변수인 ref로 전달합니다.
  • ref를 <button>의 JSX 속성으로 전달합니다.
  • 마운트가 완료되면 ref.current는 <button> DOM 노드를 가리킵니다.

HOC에서 Ref 전달 ref는 prop과 다르게 특별히 처리됩니다. 따라서 HOC에 ref를 추가하면 가장 바깥쪽 컨테이너 컴포넌트를 참조하게 되므로 React.forwardRef()를 사용해야 합니다.

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('이전 props:', prevProps);
      console.log('새로운 props:', this.props);
    }

    render() {
      const { forwardedRef, ...rest } = this.props;
      return <Component ref={forwardedRef} {...rest} />;
    }
  }

  return React.forwardRef((props, ref) => (
    <LogProps {...props} forwardedRef={ref} />
  ));
}

3. 콜백 Refs

createRef()로 생성한 ref 대신 함수를 전달할 수 있습니다. 이 함수는 React 컴포넌트 인스턴스나 HTML DOM 요소를 매개변수로 받아 이를 다른 곳에서 저장하고 접근할 수 있게 합니다.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    this.focusTextInput();
  }

  render() {
    return (
      <div>
        <input type="text" ref={this.setTextInputRef} />
        <button onClick={this.focusTextInput}>텍스트 입력 초점 맞추기</button>
      </div>
    );
  }
}

자식 컴포넌트의 Ref 콜백 전달

function CustomTextInput({ inputRef }) {
  return <input ref={inputRef} />;
}

class Parent extends React.Component {
  render() {
    return <CustomTextInput inputRef={el => (this.inputElement = el)} />;
  }
}

4. useRef와 useImperativeHandle

  • useRef
function TextInputWithFocusButton() {
  const inputEl = React.useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>텍스트 입력 초점 맞추기</button>
    </>
  );
}
  • useImperativeHandle

useImperativeHandle(ref, createHandle, [deps]) useImperativeHandle은 ref를 사용할 때 부모 컴포넌트에 노출되는 인스턴스 값을 사용자 정의할 수 있게 해줍니다. 대부분의 경우 명령형 코드를 피해야 하지만 useImperativeHandle은 forwardRef와 함께 사용해야 합니다.

function FancyInput(props, ref) {
  const inputRef = React.useRef();
  React.useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));
  return <input ref={inputRef} {...props} />;
}

FancyInput = React.forwardRef(FancyInput);

위 예시에서 부모 컴포넌트는 <FancyInput>을 렌더링할 때 inputRef.current.focus()를 호출할 수 있습니다.

태그: React Refs useRef

9월 28일 12:56에 게시됨