ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • useState와 useRef의 차이
    Frameworks, Platforms and Libraries/React 2023. 10. 29. 15:22

    주니어 개발자분께서 이메일 인증 로직에서 인증 시간을 체크하는 timer의 상태 관리에 useRef가 사용되는 걸 보시곤 "상태 관리에는 useState가 쓰이고 useRef는 DOM에 접근할 때 쓰는 것 아닌가요?"라는 질문을 받았습니다. 간단하게 컴포넌트의 생명주기에 따른 상태와 re-render 유무를 설명드렸고 이번 기회에 해당 내용을 간단하게 정리하려 합니다.

     

    useState vs useRef

    보통 useState는 특정 상태 값을 반환하거나 갱신이 필요할 때, useRef는 특정 DOM에 접근하기 위해 많이 사용합니다. 하지만 리액트 공식문서를 보면 추가로 알 수 있는 내용이 있습니다.

    useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

    Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. 

    The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose  current property is mutable and can hold any value,

     

    공식 문서를 보면 useRef도 특정 상태 값을 반환하거나 갱신이 가능합니다. 하지만 useState와 달리 컴포넌트의 생명주기 내내 최신화된 값을 바라보며 값의 갱신 시 리렌더링이 일어나지 않습니다. 해당 내용을 직접 볼 수 있게 예제 코드와 화면을 보겠습니다.

     

    import { useEffect, useRef, useState } from 'react';
    import './react-hooks.css';
    
    function ReactHooks() {
      const [countState, setCountState] = useState<number>(0);
    
      const countRef = useRef<number>(0);
    
      useEffect(() => {
        console.log('mount useState', countState);
        console.log('mount useRef', countRef.current);
    
        return () => {
          console.log('unmount useState', countState);
          console.log('unmount useRef', countRef.current);
        };
      }, []);
    
      const updateCountState = () => {
        setCountState(countState + 1);
        console.log('update useState', countState);
      };
    
      const updateCountRef = () => {
        countRef.current = countRef.current + 1;
        console.log('update useRef', countRef.current);
      };
    
      return (
        <div className="wrapper">
          <h1>React Hooks</h1>
          <div className="card">
            <button onClick={() => updateCountState()}>
              useState count is {countState}
            </button>
          </div>
          <div className="card">
            <button onClick={() => updateCountRef()}>
              useRef count is {countRef.current}
            </button>
          </div>
        </div>
      );
    }
    
    export default ReactHooks;

     

    useState와 useRef를 버튼의 클릭이벤트를 통해 각 값의 변화를 일으키며 컴포넌트 mount, unmount, 값의 변화가 있을 경우 console을 출력하도록 했습니다.

     

    React Developer Tools를 통해 각 변화 시 리렌더링 체크를 하면 useState의 업데이트 시에는 리렌더링이 발생하였지만 useRef의 업데이트 시에는 리렌더링이 발생하지 않았습니다.

     

    콘솔 창을 보면 두 가지 의문점이 듭니다. 첫 번째는 useState 값의 변화 시 왜 변화 전 값이 출력되는지, 두 번째는 useEffect cleanup 동작시 useState는 초기값이 출력되었지만 useRef는 변화된 값이 출력되는지입니다.

     

    첫 번째의 현상은 useState의 값의 변화와 re-render의 과정이 비동기적이기 때문에 해당 메서드가 동작한 후 변화된 상태 값이 적용됩니다. 그래서 console.log에는 변화가 일어나기 전의 값이 출력됩니다.

     

    두 번째의 현상은 useEffect가 생성되는 시점의 useState의 초기값이 들어간 이후 재생성되지 않았기 때문에 초기값이 출력이 되지만 useRef의 상태 값의 경우 컴포넌트의 생명주기 내내 업데이트된 값을 바라보기 때문에 변화된 값이 출력이 됩니다.

     

    결론

    useState는 컴포넌트의 생명주기와 연관이 있고 상태 변화 시 리렌더링이 발생하지만 useRef는 생명주기 내내 최신화된 값을 가지며 리렌더링이 발생하지 않습니다. 따라서 각각의 상황에 맞게 hooks을 잘 사용해야 되겠습니다. 그리고 리액트 공식문서의 내용이 상세하게 잘 나와있어 그동안 꼼꼼하게 읽지 않았던 점을 반성해야 되겠습니다.


    참고 자료

    https://reactjs.org/docs/hooks-intro.html

    https://medium.com/humanscape-tech/react-usestate-vs-useref-4c20713f7ef

Designed by Tistory.