React - useRef

sarang_daddy·2023년 6월 20일
0

React

목록 보기
8/26
post-thumbnail

useRef란?

useRef는 렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook이다.

// 초기 셋팅
import { useRef } from 'react';

function MyComponent() {
  const intervalRef = useRef(0); // 초기값 
  const inputRef = useRef(null); // 초기값
  // ...
  • useRef는 단일 프로퍼티를 가진 객체를 반환한다.
  • 처음에는 초기값으로 설정된 이후 다른 값으로 설정할 수 있다.
  • 다음 렌더링에서 useRef는 동일한 객체를 반환한다.
  • 현재 프로퍼티를 변경하여 정보를 저장하고 나중에 사용할 수 있다.
  • state와의 차이점은 값이 변경되어도 재렌더링이 되지 않는다.
  • ref는 시각적 출력에 영향을 주지 않는 정보를 저장하는 데 적합하다.

사용 예시

setinterval() 함수는 주어진 시간 간격마다 실행되는데, 매 호출 시마다 고유한 식별자인 interalId값을 반환한다. 이 interalId값을 ref로 저장했다가 나중에 사용할 수 있다.

function handleStartClick() {
  const intervalId = setInterval(() => {
    // ...
  }, 1000);
  intervalRef.current = intervalId;
}

// ref값에 저장된 interalId값으로 반복실행을 제거한다.
function handleStopClick() {
  const intervalId = intervalRef.current;
  clearInterval(intervalId);
}
  • 렌더링마다 재설정되는 일반 변수와 달리 재렌더링 사이에 정보를 저장할 수 있다.
  • 이 정보는 공유되는 외부 변수와 달리 컴포넌트의 각 사본에 로컬로 저장된다.

useRef를 이용한 스탑워치

// setInterval 함수가 반환하는 타이머 식별자를 저장하기 위해 사용된 useRef

import { useState, useRef } from 'react';

export default function Stopwatch() {
  const [startTime, setStartTime] = useState(null); // 화면에 출력되는 상태 변수
  const [now, setNow] = useState(null); // 화면에 출력되는 상태 변수
  const intervalRef = useRef(null); // 화면에 출력되지 않고 렌더링되어도 변하지 않는다.

  function handleStart() {
    setStartTime(Date.now());
    setNow(Date.now());

    clearInterval(intervalRef.current); // 이전에 설정된 타이머를 제거
    intervalRef.current = setInterval(() => {
      setNow(Date.now()); // ref는 새로운 타이머의 식별자를 참조한다.
    }, 10);
  }

  function handleStop() {
    clearInterval(intervalRef.current); // stop을 누르면 설정된 타이머를 제거한다.
  }

  let secondsPassed = 0;
  if (startTime != null && now != null) {
    secondsPassed = (now - startTime) / 1000;
  }

  return (
    <>
      <h1>Time passed: {secondsPassed.toFixed(3)}</h1>
      <button onClick={handleStart}>
        Start
      </button>
      <button onClick={handleStop}>
        Stop
      </button>
    </>
  );
}
  • useRef는 렌더링 중에 무언가를 읽거나 써야하는 경우에는 사용하면 안된다. (state 사용)
  • event handlers나 useEffects에서 사용하면 좋다.

useRef로 DOM 조작하기

useRef는 참조를 사용해 DOM을 조작하는데 사용된다.

// 먼저 초기값이 null인 참조 객체를 선언한다.

import { useRef } from 'react';

function MyComponent() {
  const inputRef = useRef(null);
  // ...
// 그런 다음 참조 객체를 참조 어트리뷰트로 조작하려는 DOM 노드의 JSX에 전달한다.

  // ...
  return <input ref={inputRef} />;
// React가 DOM 노드를 생성하고 화면에 배치하면 React는 참조 객체의 현재 프로퍼티를 해당 DOM 노드로 설정한다.
// 이제 <input>의 DOM 노드에 접근하여 focus()와 같은 메서드를 호출할 수 있다.

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

useRef를 이용한 focus

// useRef로 버튼 focus 주기

import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

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

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>

useRef를 이용한 이미지 슬라이더

// useRef를 이용한 이미지 슬라이더

import { useRef } from 'react';

export default function CatFriends() {
  const listRef = useRef(null); // ul를 참조하기 위한 useRef

  function scrollToIndex(index) {
    const listNode = listRef.current; // ul 요소에 대한 참조
    const imgNode = listNode.querySelectorAll('li > img')[index]; // ul 자식의 img 태그를 선택
    imgNode.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest',
      inline: 'center'
    }); // 선택된 img를 가운데로 스크롤한다.
  }

  return (
    <>
      <nav>
        <button onClick={() => scrollToIndex(0)}>
          Tom
        </button>
        <button onClick={() => scrollToIndex(1)}>
          Maru
        </button>
        <button onClick={() => scrollToIndex(2)}>
          Jellylorum
        </button>
      </nav>
      <div>
        <ul ref={listRef}>
          <li>
            <img
              src="https://placekitten.com/g/200/200"
              alt="Tom"
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/300/200"
                 alt="Maru"
            />
          </li>
          <li>
            <img
              src="https://placekitten.com/g/250/200"
              alt="Jellylorum"
            />
          </li>
        </ul>
      </div>
    </>
  );
}

useRef로 최적화하기

React는 초기 참조 값을 한 번 저장하고 다음 렌더링에서 이를 무시한다.
이를 활용하여 불필요한 생성 등의 낭비를 방지 할 수 있다.

function Video() {
  const playerRef = useRef(new VideoPlayer());
  // ...

새로운 VideoPlayer()의 결과는 초기 렌더링에만 사용되지만, 여전히 모든 렌더링에서 이 함수를 호출하게 된다. 이는 값비싼 객체를 생성하는 낭비가 된다.

이 문제를 해결하려면 다음과 같이 참조를 초기화할 수 있다.

function Video() {
  const playerRef = useRef(null); // playerRef 변수 생성 후 null로 초기화
  if (playerRef.current === null) {
    playerRef.current = new VideoPlayer(); // null인 경우 videoPlayer 인스턴스 저장
  }
  // ...
  • 위 방법으로 초기 렌더링에만 VideoPlayer인스턴스르 생성하고 이후에는 재사용할 수 있다.
  • playerRef.current.play()와 같은 방법으로 VideoPlayer의 기능을 사용할 수 있다.
  • 최초 한번만 인스턴스를 생성하고 이후에는 렌더인이 되더라도 Ref에 저장된 인스턴스를 사용 가능하다.

참고자료

useRef
Refs로 참조하기
Ref로 DOM 조작하기

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글