1. useRef

useRef는 순수 자바스크립트 객체를 생성합니다. 값의 접근은 refContainer.current로 할 수 있습니다.

const refContainer = useRef(initialValue);

useRef는 크게 2가지로 사용이 됩니다.

  1. useState와는 다르게 리렌더링을 발생시키지 않고 값을 저장하거나 변경하고 싶을 때 useRef를 사용합니다. 다시 렌더링 되어도 useRef를 통해 저장된 값은 변하지 않습니다.

  2. 또한 컴포넌트의 태그(tag)에 직접 접근하고 싶을 때 useRef를 사용합니다. 자바스크립트에서 DOM element를 가지고 올 때 querySelector나 getElementById를 사용하지만 React에서는 useRef를 사용합니다.

    이때 useRef를 사용하여 useRef가 접근한 태그 요소의 값을 바꿀 때는 리렌더링을 발생시키지 않는다는 것을 기억해야합니다. 데이터, state와 상관없이(리렌더링 하지 않고) component에 DOM을 제어하고 싶을 때 ref를 사용합니다.

useRef는 남용하지 않습니다. 먼저 ref를 사용하지 않고도 다른 기능을 사용하여서 구현할 수 있는지 고려한 후 사용하세요. 저장된 값은 컴포넌트가 다시 랜더링 되어도 변하지 않기 때문에 값이 계속 누적됩니다. 누적된 값은 결국 앱에 부담을 주게됩니다.

React에서도 return문에 있는 요소들에게 id값을 주고 선택자 함수를 사용해도 좋지만(기존에 자바스크립트 방식), React에 가장 큰 장점인 재사용성을 생각한다면 사용을 권장하지 않습니다.

컴포넌트를 재사용할 경우 동일한 id값을 가진 요소가 생기기 때문입니다. 재사용 안한다 하더라도 직접 id를 사용하는 경우 중복의 경우가 생길 수 있습니다.

그래서 id를 사용하는 대신 ref를 사용합니다. ref는 전역으로 작동하지않고 컴포넌트 내부에서만 작동하기 때문입니다.(재사용할 때 같은 컴포넌트지만 다르게 작동합니다.) 만약 id를 사용하고자 한다면 컴포넌트를 만들 때마다 id 뒷부분에 id name을 추가하여 유니크한 id로 만들어줘야 합니다.

2. useRef와 다른 변수들의 차이

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

function Counter() {
  const [count, setCount] = useState(0);
  let countRef = useRef(0);
  let countConst = 0;

  // 랜더링 됨
  const handleCountUpState = (e) => {
    setCount(count + 1)
    console.log(count) // 비동기 -> useEffect로 추적하면 동기 가능
  }
 
  // 클릭하면 변수의 값은 증가하지만 랜더링은 되지 않음
  const handleCountUpRef = (e) => {
    countRef.current = countRef.current + 1
    console.log(countRef.current)
  }
  // 랜더링 안되고 다른 state가 변해서 재렌더링 되어도 0으로 초기화
  const handleCountUpConst = (e) => {
    countConst = countConst + 1
    console.log(countConst)
  }

  return(
    <>
      <div>{count}</div>
      <button onClick={handleCountUpState}>up!</button>
      <div>{countRef.current}</div>
      <button onClick={handleCountUpRef}>up!</button>
      <div>{countConst}</div>
      <button onClick={handleCountUpConst}>up!</button>
    </>
  )
}

function App() {
  return (
    <div>
      <Counter/>
    </div>
  );
}

export default App;

3. useRef를 사용하지 않은 방식

여러가지 시도를 해보세요. 아래 주석된 것을 풀고 진행해보시면 error가 나오는데요. 경고메시지도 한 번 확인해보시길 권해드립니다. 영상에서는 경고가 없는 완성된 코드만을 설명합니다.

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

const App = () => {
  const [emailValue, setEmailValue] = useState(""); // email state 값
  const [pwValue, setPwValue] = useState(""); // pw state 값
  // const a = ''
  // const b = ''

  const inputCheck = (e) => {
    e.preventDefault()
    console.log(document.querySelectorAll('input'))
    setEmailValue(document.querySelectorAll('input')[0].value)
    setPwValue(document.querySelectorAll('input')[1].value)
    // setEmailValue(emailInput.current.value)
    // setPwValue(pwInput.current.value)
  };

  return (
    <form style={{ display: "flex", flexDirection: "column" }} onSubmit={inputCheck}>
      <label>
        이메일 : <input type="email" />
        {/* 이메일 : <input type="email" ref={a} /> */}
        {/* 이메일 : <input type="email" ref={emailValue}/> */}
      </label>
      <label>
        비밀번호 : <input type="password" />
        {/* 비밀번호 : <input type="password" ref={b} /> */}
        {/* 비밀번호 : <input type="password" ref={pwValue} /> */}
      </label>

      <button type="submit" style={{ width: "100px" }} >
        로그인
      </button>
      <span>입력한 이메일 : {emailValue}</span>
      <span>입력한 비밀번호 : {pwValue}</span>
    </form>
  );
};

export default App;

4. useRef 사용해 봅시다!