본문 바로가기

Sparta x 이노베이션 캠프/React

TIL: useRef

반응형

useRef() , 왜 필요한가?

 

React 컴포넌트는 기본적으로 내부 상태(state)가 변할 때 마다 렌더링(rendering)이 된다. 버튼을 누를 때 숫자가 증가하고 감소하는 카운터의 경우, 버튼을 누르는 만큼 상태값이 바뀌기 때문에 같은 횟수로 리렌더링이 된다. 카운터의 컴포넌트 함수는 상태가 바뀔 때 마다 호출이 된다는 말은 함수 내부의 변수들이 모두 초기화되고 함수의 모든 로직이 다시 실행된다는 것을 의미.

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  console.log(`랜더링... count: ${count}`);

  return (
    <>
      <p>{count}번 클릭하셨습니다.</p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </>
  );
}
랜더링... count: 1
랜더링... count: 2
랜더링... count: 3
랜더링... count: 4
랜더링... count: 5

콘솔을 확인 하면 5번의 로그가 찍힘.

 

다시 랜더링 되어도 동일한 참조값을 유지하려면?

대부분, 위와 같이 상태가 변하면 React 컴포넌트 함수가 호출되어 화면이 갱신되기를 바라지만 그에 따른 부작용으로 함수 내부의 변수들이 기존에 저장하고 잇는 값들을 잃어버리고 초기화 됨 => 하지만 리렌더링 되더라도 기존에 참조하던 컴포넌트 함수 내의 값이 그대로 보존되어야 하는 경우가 있다.

import React, { useState, useEffect } from "react";

function AutoCounter() {
  const [count, setCount] = useState(0);
  console.log(`랜더링... count: ${count}`);

  useEffect(() => {
    const intervalId = setInterval(() => setCount((count) => count + 1), 1000);
    return () => clearInterval(intervalId);
  }, []);

  return <p>자동 카운트: {count}</p>;
}

여기서 만약 카운트를 자동으로 시작하지 않고 버튼을 이용해 시작하고 정지하고 싶으면?

 

import React, { useState, useEffect } from "react";

function ManualCounter() {
  const [count, setCount] = useState(0);

  let intervalId;

  const startCounter = () => {
    // 💥 매번 새로운 값 할당
    intervalId = setInterval(() => setCount((count) => count + 1), 1000);
  };
  const stopCounter = () => {
    clearInterval(intervalId);
  };

  return (
    <>
      <p>자동 카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
}

여기서 가장 큰 문제는 안에 선언된 intervalId 변수를 startCounter()함수와 stopCounter()함수가 공유할 수 있도록 해줘야 한다는 것이다. 그럴려면 intervalId 변수를 두 함수 밖에서 선언해야하는데 그러면 count 상태값이 바뀔 때 마다 컴포넌트 함수가 호출되어 intervalId 또한 매번 새로운 값으로 바뀜. 브라우저 메모리에는 미처 정리되지 못한 intervalId들이 1초에 하나씩 쌓임.

 

useRef사용하기

useRef는 current 속성을 가지고 있는 객체를 반환하는데, 인자로 넘어온 초기값을 current 속성에 할당. 이 current 속성은 값을 변경해도 상태를 변경할 때 처럼 React 컴포넌트가 다시 랜더링 되지 않는다. React 컴포넌트가 다시 랜더링 될 때도 마찬가지로 이 current 속성의 값이 유실되지 않음.

 

 

useRef 훅 함수가 반환하는 객체의 이런 독특한 성질을 이용해 startCounter(), stopCounter()함수를 구현

import React, { useState, useRef } from "react";

function ManualCounter() {
  const [count, setCount] = useState(0);
  const intervalId = useRef(null);
  console.log(`랜더링... count: ${count}`);

  const startCounter = () => {
    intervalId.current = setInterval(
      () => setCount((count) => count + 1),
      1000
    );
    console.log(`시작... intervalId: ${intervalId.current}`);
  };

  const stopCounter = () => {
    clearInterval(intervalId.current);
    console.log(`정지... intervalId: ${intervalId.current}`);
  };

  return (
    <>
      <p>자동 카운트: {count}</p>
      <button onClick={startCounter}>시작</button>
      <button onClick={stopCounter}>정지</button>
    </>
  );
}

 

 

 

 

https://www.daleseo.com/react-hooks-use-ref/

 

React Hooks: useRef 사용법

Engineering Blog by Dale Seo

www.daleseo.com

 

 

 

반응형