[React] useRef 파헤치기

Narcoker·2023년 9월 6일
0

React

목록 보기
26/32
post-custom-banner

useRef 란 무엇일까?

React Hook 중의 하나로, 주로 DOM 요소에 대한 참조를 저장하거나
렌더링 사이의 값을 유지하기 위해 사용된다.

🗣 렌더링 사이의 값을 유지하기 위해 사용한다?

컴포넌트가 다시 렌더링 될때마다 useRef로 생성된 ref 객체가 유지된다는 의미한다.
이를 통해 이전 렌더링에서의 정보나 상태를 기억할 수 있다.

useRef 의 값은 페이지가 언마운트 되면 사라진다.
즉, useState와 생명 주기가 같다.

useRefref 객체를 반환하고
이 객체는 current 라는 프로퍼티가 있다.

🗣 ref 객체 란?

useRef 또는 createRef 와 같은 API를 사용해서 생성된 객체로,
주로 DOM 요소나 React 컴포넌트에 대한 참조를 저장한다.

ref 객체current 라는 프로퍼티를 가지고 있으며
current 프로퍼티에 DOM 요소나 다른 값을 할당할 수 있다.

기본 구조

{
  current: null // 또는 다른 값
}

const number = useRef(0)

사용 예시 - DOM 요소 접근

import React, { useRef, useEffect } from 'react';

const MyComponent: React.FC = () => {
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    // DOM 요소에 직접 접근하여 포커스 설정
    if (inputRef.current !== null) {
      inputRef.current.focus();
    }
  }, []);

  return <input ref={inputRef} />;
};
  • 컴포넌트를 React.FC 타입으로 명시
  • useRef를 호출할 때, <HTMLInputElement | null> 타입을 지정
    • 이는 input DOM 요소를 참조하거나 null 값을 가질 수 있음을 명시합니다.
  • useEffect 내에서 inputRef.current에 접근하기 전에 null 체크
    • 이는 타입스크립트가 null 또는 undefined 값에 대해 안전하게 코드를 작성하도록 강제하기 때문입니다.

사용 예시 - 타이머 ID 유지하기

import React, { useRef, useEffect } from 'react';

const Timer: React.FC = () => {
  const timerID = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    timerID.current = setInterval(() => {
      console.log('1초 경과');
    }, 1000);

    return () => {
      if (timerID.current) {
        clearInterval(timerID.current);
      }
    };
  }, []);

  return (
    <div>
      <button onClick={() => {
        if (timerID.current) {
          clearInterval(timerID.current);
        }
      }}>타이머 중지</button>
    </div>
  );
};
  • 컴포넌트를 React.FC 타입으로 명시
  • useRef<NodeJS.Timeout | null>(null)에서는
  • timerID.currentNodeJS.Timeout 객체 또는 null을 가질 수 있음을 타입으로 명시
    • 타이머 ID는 NodeJS의 setInterval 함수로부터 반환 되기 때문에
      NodeJS.Timeout 타입을 사용합니다.
  • useEffect와 버튼의 onClick 이벤트 핸들러에서
    if (timerID.current)를 사용하여 timerID.current 값이 null 또는 undefined인지 체크
    • 타입스크립트에서는 이러한 체크 없이 null이나 undefined에 접근하려 하면 오류를 발생시킬 수 있습니다.
  • useEffect에서 반환하는 함수를 통해 타이머를 중지합니다.
    • 이 클린업 함수는 컴포넌트가 언마운트될 때 자동으로 호출됩니다.
    • 이 방법을 통해 리소스 누수를 방지할 수 있습니다.

🗣 useRef 에 타이머 ID 를 저장하지 않고 useState일반 변수에 저장하면 안될까?

useState 로 타이머 ID를 관리하는 예제를 살펴보자

const [timerID, setTimerID] = useState(null);

useEffect(() => {
  const id = setInterval(() => {
    console.log('1초 경과');
  }, 1000);

  setTimerID(id);

  return () => {
    clearInterval(id);
  };
}, []);

timerId 가 먼저 null 로 초기화 되고 페이지가 렌더링된 이후
useEffect 가 실행된다.

그럼 timerId 값, 즉 상태가 변경되므로 재렌더링이 일어난다.
이는 불필요한 렌더링을 초래한다.


일반 변수에 저장하는 예제를 살펴보자

useEffect(() => {
  const timerID = setInterval(() => {
    console.log('1초 경과');
  }, 1000);

  return () => {
    clearInterval(timerID);
  };
}, []);

일반 변수를 useEffect 콜백 함수 내부에 선언했고 타이머 ID를 할당했다.
useState와 달리 재렌더링을 초래하지 않는다.

쓰면 안된다기보다는 단점이 있다.
timerID 의 스코프를 잘 살펴보자. -> ⭐️⭐️⭐️콜백함수⭐️⭐️⭐️ 내부에 있다.
일반 변수의 최대 스코프 범위는 함수 레벨 스코프이다.

즉, 외부에서 timerID 를 참조할 수 없다.
다른 말로 하면, Button 의 onClick 함수를 통해 타이머를 중지시킬 수 없다는 뜻이다!!!

사용 예시 - DOM Element에 ref Prop으로 넘겨줄 때

import { useRef } from "react";

const inputRef = useRef(null);;

// <Input inputRef={inputRef} /> bad
<Input label="input 컴포넌트  분리" ref={inputRef} />

import { forwardRef } from "react";

const Input = forwardRef(function Input(
  props: { label: string },
  ref: React.ForwardedRef<HTMLInputElement>,
) {
  const { label } = props;
  return (
    <div>
      <span>{label}</span>
      <input ref={ref} />
    </div>
  );
});

export default Input;

이 방식을 사용하면 Input 컴포넌트를 분리해서
전체 렌더링이 아닌 부분 렌더링을 구현할 수 있다.

profile
열정, 끈기, 집념의 Frontend Developer
post-custom-banner

0개의 댓글