Front End/React

useRef

DOM reference를 활용할 수 있는 useRef

React만 가지고 거의 대부분의 프론트엔드 요구사항을 구현할 수 있어 DOM 지식이 필요없다고 생각할 수 있지만, React로 모든 개발 요구 사항을 충족할 수는 없다. 아래처럼 DOM 엘리먼트의 주소값을 활용해야 하는 경우 특히 그렇다.

  • focus
  • text selection
  • media playback
  • 애니메이션 적용
  • d3.js, greensock 등 DOM 기반 라이브러리 활용

React는 이런 예외적인 상황에서 useRef로 DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있다. 아래의 예시코드를 보자.

const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있다.
return (
    <div>
      <input ref={주소값을_담는_그릇} type="text" />
        {/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면 */}
        {/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담긴다 */}
        {/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있다 */}
    </div>);

이 주소값은 컴포넌트가 re-render 되더라도 바뀌지 않는다. 이 특성을 활용해 아래의 제한된 상황에서 useRef를 활용할 수 있다.

function TextInputWithFocusButton() {
	// 고정된 DOM 엘리먼트의 주소를 담는 변수
  const inputEl = useRef(null);
	// 버튼 클릭 시 실행되는 콜백함수(이벤트 핸들러)
  const onButtonClick = () => {
		// DOM Element인 input에 커서를 두는 메서드
    inputEl.current.focus();
  };
  return (
    <>
			{// 컴포넌트에 ref 속성을 넣고 값으로 useRef를 할당한 inputEl을 넣으면}
			{// input 컴포넌트의 DOM 주소가 inputEl에 담긴다}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>);
}

제시된 상황을 제외한 대부분의 경우 기본 React 문법을 벗어나 useRef를 남용하는 것은 부적절하고 React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용하여야 한다.

예제 1 : focus

import React, { useRef } from "react";

const Focus = () => {
	// 고정된 DOM 엘리먼트의 주소를 담는 변수
  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);

	// input 내에서 Enter 키를 입력하면 실행되는 콜백함수
  const handleInput = (event) => {
    console.log(event.key, event);
    if (event.key === "Enter") {
			// 이벤트가 발생한 주소와 첫 번째 input의 DOM 엘리먼트 주소가 같다면
      if (event.target === firstRef.current) {
				// 두번째 인풋으로 커서를 이동하고
        secondRef.current.focus();
				// 첫번째 인풋에 입력된 값을 지운다
        event.target.value = "";
      } else if (event.target === secondRef.current) {
        thirdRef.current.focus();
        event.target.value = "";
      } else if (event.target === thirdRef.current) {
        firstRef.current.focus();
        event.target.value = "";
      } else {
        return;
      }
    }
  };

  return (
    <div>
      <h1>타자연습</h1>
      <h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
      <div>
        <label>hello </label>
        <input ref={firstRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>world </label>
        <input ref={secondRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>codestates </label>
        <input ref={thirdRef} onKeyUp={handleInput} />
      </div>
    </div>
  );
};

export default Focus;

예제 2 : media playback

import { useRef } from "react";

export default function App() {
	// 고정된 DOM 엘리먼트의 주소를 담는 변수
  const videoRef = useRef(null);

	// Play 버튼을 클릭하면 실행하는 콜백 함수
  const playVideo = () => {
		// video의 주소를 가져와서 play 메서드 실행
    videoRef.current.play();
    console.log(videoRef.current);
  };

	// Pause 버튼을 클릭하면 실행하는 콜백 함수
  const pauseVideo = () => {
		// video의 주소를 가져와서 pause 메서드 실행
    videoRef.current.pause();
    videoRef.current.remove();
  };

  return (
    <div className="App">
      <div>
        <button onClick={playVideo}>Play</button>
        <button onClick={pauseVideo}>Pause</button>
      </div>
      <video ref={videoRef} width="320" height="240" controls>
        <source
          type="video/mp4"
          src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
        />
      </video>
    </div>
  );
}

Uploaded by N2T