본문 바로가기

개념/React

[React] useRef사용법, 언제 써야 할까?

🤔 useRef는

 

javaScript에서는 DOM을 선택해야 하는 상황에 getElementById나 querySelector와 같은 DOM Selector 함수를 사용해서 선택했다.

그런데 React에선 실질적으로 대부분 state를 이용해 자동으로 리랜더링 되기 때문에, 직접 DOM요소를 건드릴 일이 많이 없고,

리액트 자체에서도 DOM을 되도록 직접 조작하는 일은 없게하라 그런다.

 

그럼에도 불구하고, DOM을 직접 선택해야 하는 상황이 있다.

예를 들면 아래와 같은 상황들이다.

 

useRef가 필요한 상황 예시

  • 특정 엘리먼트의 크기 가져오기
  • 스크롤바의 현 위치 가져오기 혹은 설정하기
  • 🌟포커스 설정하기
  • 🌟비디오 제어하기
  •  Video.js, JWPlayer 같은 HTML5 Video 관련 라이브러리 사용할 때
  • D3, chart.js 같은 그래프 관련 라이브러리 사용할 때
  • greensock, anime등 애니메이션 관련 라이브러리 사용할 때

위 사례들을 대충 머리속에 그려놓고 있는게 도움이 된다.

 

참고로, 클래스형 컴포넌트에선 ref를 사용하는데, 이를 함수형 컴포넌트에서 사용가능하도록 한것이 useRef Hook 이다.

useRef를 사용하면 컴포넌트가 re-render되더라도 주소값이 바뀌지 않기 때문에, 안정적으로 DOM을 활용할 수 있다. 


🌟ex01:  input에 focus 넘기기

 

대표적으로 useRef를 사용해야하는 focus의 예시를 보며 방법을 설명해 보겠다.

아래는 우리가 흔히 보는 아이디창에서 아이디를 입력하고 enter키를 누르면 password입력란으로 자동 focus가 되는 기능을 추가하는 코드이다.

 

 

1. 리액트를 불러오면서 useRef도 함께 불러온다.

 

import React, { useRef } from "react";

 

 

 

2. 임의의 변수에 useRef()를  할당해서 DOM 참조값을 담을 그릇을 만들어준다.

 

 const nameInput = useRef();
 
 import React, { useRef } from "react";

const SignInForm = () => {

  const idRef = useRef(); //📌 id input DOM값을 담을 변수를 useRef를 사용해서 만들어준다.
  const passwordRef = useRef(); //📌 password input DOM값을 담을 변수를 useRef를 사용해서 만들어준다.
  
  return (
    <div>
      <div>
        <label>ID </label>
        <input onKeyUp={handleInput} /> 
      </div>
      <div>
        <label>PASSWORD </label>
        <input onKeyUp={handleInput} />
      </div>
    </div>
  );
}

export default SignInForm;

 

 

3. 이제 return 문 내부 엘레먼트 요소에 ref={useRef()담긴 변수명}을 할당해서,

이 요소의 참조값을 이 변수에 넣으면 돼! 하고 알려준다.

 

import React, { useRef } from "react";

const SignInForm = () => {

  const idRef = useRef();
  const passwordRef = useRef();
  
  return (
    <div>
      <div>
        <label>ID </label>
        <input ref={idRef} onKeyUp={handleInput} /> 
        //📌 조작하기 원하는 엘레먼트에 ref={변수값}로 이 요소의 참조값을 해당 변수에 넣으라고 알려준다.
        // 그럼 이제 이 코드를  위의 idRef 변수에 넣어준다.
      </div>
      <div>
        <label>PASSWORD </label>
        <input ref={passwordRef} onKeyUp={handleInput} />
        //📌 조작하기 원하는 엘레먼트에 ref={변수값}로 이 요소의 참조값을 해당 변수에 넣으라고 알려준다.
        // 그럼 이제 이 코드를  위의 ipasswordRef 변수에 넣어준다.
      </div>
    </div>
  );
}

export default SignInForm;

 

 

4. 이제 해당 DOM을 조작하는 함수에서 변수로 DOM을 불러와 조작할 수 있다.

 

import React, { useRef } from "react";

const SignInForm = () => {

  const idRef = useRef();
  const passwordRef = useRef();
  
  const handleInput = (event) => {
    if (event.key === "Enter") {
      if (event.target === idRef.current) { //📌idRef내부 current에 해당 DOM의 참조값이 들어있다.
        passwordRef.current.focus();
      } else {
        return;
      }
    }
  };
  
  return (
    <div>
      <div>
        <label>ID </label>
        <input ref={idRef} onKeyUp={handleInput} /> 
      </div>
      <div>
        <label>PASSWORD </label>
        <input ref={passwordRef} onKeyUp={handleInput} />
      </div>
    </div>
  );
}

export default SignInForm;

 

이경우에 state를 써도 어떻게든 되긴 하겠지만, 문제는 state를 썼을 때 번거로운 일이 많다는 것이다.

state를 쓸경우 state가 변경되면 input이 리랜더링 될 것이고, 그럼 이미 써놓은 내용은 다시 지워지니까.

또 그 내용을 저장했다가 다시 적어줄 수 있도록 설계해야 할것이다.

 

이런 경우 DOM이 더 편하기 때문에 우린 focus를 제어할때, useRef를 사용한다.


🌟ex02: 비디오를 멈추고 다시 재생하기

 

import { useRef } from "react";

export default function App() {
  const videoRef = useRef(null); //📌 참조값을 담을 변수를 만들어준다.

  const playVideo = () => {
    videoRef.current.play();  //📌 current에 들어있는 DOM참조값을 써서 play()시켜준다.
  };

  const pauseVideo = () => {
    videoRef.current.pause(); //📌 current에 들어있는 DOM참조값을 써서 pause()시켜준다.
  };

  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>
  );
}

비디오 역시 state로 재생상태를 제어한다면? 리랜더링되어서 멈출때마다 어디까지 재생되었고 멈췄는지 다 처리해줘야한다.

이럴경우 그냥 DOM을 사용하는 것이 낫기때문에 우린 useRef를 사용한다.

 

🛑 위 focus 예시에서도 그랬지만 변수.current에 DOM주소가 들어있다는 사실에 유의해야한다.

 

 


 

참고링크

 

https://react.vlpt.us/basic/10-useRef.html

https://ko.reactjs.org/docs/uncontrolled-components.html

https://ko.reactjs.org/docs/refs-and-the-dom.html