useRef vs variable, useState 차이점

빡기·2020년 4월 13일
3

Hooks를 이용해 개발하던 도중 일반 변수(variable)와 useRef의 차이점에 대해 궁금해져서 공부를 한 내용을 정리해보자 한다.

1. useRef vs useState

1-1 useState를 사용한 예시 코드

function Counter() {
  const [counter, setCounter] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => {
      setCounter(counter => counter + 1);
    }, 1000);
    return () => {
      clearInterval(timer);
      alert(counter);
    };
  }, []);
  return (
    <div>
      <p>{counter}</p>
    </div>
  );
}

위의 코드대로 작성하면 counter가 1초마다 증가하는 rendering은 정상적으로 나타나지만 alert은 항상 0으로 출력된다.
useEffect의 dependency에 counter가 의존 되지 않는 상태이기에 아래 같이 변경해줘야 한다.

useEffect(() => {
  const timer = setInterval(() => {
    setCounter(counter => counter + 1);
  }, 1000);
  return () => {
    clearInterval(timer);
    alert(counter);
  };
}, [counter]);

하지만 위의 코드대로 수정하면 이렇게 만들어버리면 counter가 변할 때마다 useEffect가 trigger되니까 원래 로직이랑 달라지게 되며 이러한 현상은 useState가 closure 안의 값까지 업데이트해줄 수는 없는 것으로 설명된다.

1-2 useRef를 사용한 예시 코드

위의 useState를 이용한 코드를 보면 counter가 변할 때 마다 렌더를 하고 싶은 경우

const value = useRef(0);
const [count, setCount] = useState(value.current);

위의 코드를 사용해서 적용하고
매번 렌더할 필요가 없는 아래와 같은 경우는

const counter = useRef(0) 

이렇게 작성하여 코딩을 하면 될 것이다.

위의 useState를 사용한 예시 코드는 굳이 리렌더를 할 필요가 없기에
후자 코드인 const counter = useRef(0)를 사용하면 될 것 같다.

function CounterKai() {
  const counter = useRef(0);
  useEffect(() => {
    const timer = setInterval(() => {
      counter.current += 1;
    }, 1000);
    return () => {
      clearInterval(timer);
      alert("<CounterKai/>:", counter.current);
    };
  }, []);
  return (
    <div>
      <p>{counter.current}</p>
    </div>
  );
}

2. useRef vs variable 비교

2-1 variable를 사용한 예시 코드

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

// defined a variable outside function component
let countCache = 0;

function Counter() {
  const [count, setCount] = useState(0);
  countCache = count;       // set default value

  useEffect(() => {
    setTimeout(() => {
      // We can get the latest count here
      console.log(`You clicked ${countCache} times (countCache)`);
    }, 3000);
  });
  // ...
}

export default Counter;
  • 위와 같이 let variable을 사용하면 자바스크립트 클로저 기능으로 인해 Counter가 소멸되면서 전역 변수인 let countCache를 참조하여 항상 같은 값인 0을 가리키게 되어 원하는 결과를 도출하지 못한다

2-2 useRef를 사용한 예시 코드

function Example() {
  const [count, setCount] = useState(0);
  const latestCount = useRef(count);

  useEffect(() => {
    // Set the mutable latest value
    latestCount.current = count;
    setTimeout(() => {
      // Read the mutable latest value
      console.log(`You clicked ${latestCount.current} times`);
    }, 3000);
  });
  // ...
}

useRef는 공식문서에 의하면 아래와 같이 정의 된다.

useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다.
이 기능은 클래스에서 인스턴스 필드를 사용하는 방법과 유사한 어떤 가변값을 유지하는 데에 편리합니다.

위의 문장처럼 useRef는 컴포넌트의 전 생명주기를 통해 유지되는 값이라는 의미이며, 순수한 자바스크립트 객체를 생성 및 유지시켜주기 때문에 closure 이슈가 발생하지 않는다는 걸 알 수 있다


profile
Front End. Dev

0개의 댓글