본문 바로가기
Web/React

[React🌀] Ref 에 대한 고찰 🔍 / 2️⃣ - useRef 와 useState, 그리고 global Variable

by 서상혁 2022. 5. 15.

들어가며

 

이전 포스팅을 읽고 오시는 것을 추천드립니다 !

https://programming119.tistory.com/265

 

[React🌀] Ref 에 대한 고찰 🔍 / 1️⃣ - Ref 의 활용과 useRef

Ref는 언제 사용할까? ref 는 references 의 약자로,  React 에서 특정 컴퍼넌트를 접근하는 데에 사용하는 props 라고 이해하고 있으면 편합니다. 리액트에서 DOM을 컨트롤할때 주로 이 ref 를 이용하지

programming119.tistory.com

 

 

useRef와 useState

 

[React🌀] Ref 에 대한 고찰 🔍 / 1️⃣ - Ref 의 활용과 useRef 를 읽고 오셨다면, 사실 두 훅을 구분하는 것은 너무나도 간단한 일입니다.

useState는 리액트의 핵심 훅이죠, state(상태)를 선언하는 훅입니다. 리액트에서 state란 리렌더링에 관여하는 변수를 의미하죠.

따라서, useState로 생성한 변수가 아닌 이상, 다른 변수들은 값이 변경되더라도 컴퍼넌트의 리렌더링을 발생시키지 않습니다.

 

useRef 가 그 일례이죠. useRef 가 함수형 컴퍼넌트에서 변수를 사용할 때 종종 쓰이긴 하지만, 컴퍼넌트의 리렌더링이 필요할 때는 사용하는 것이 의미가 없습니다.  

 

 

위 예시로 테스트해보시면 감이 오실겁니다. refCount.current 값이 올라가는 것은, 화면에 바로 렌더링되지 않습니다. state 가 변화됐을 때, refCount.current 의 값도 마찬가지로 화면에 드러나게 되죠!

 

 

useRef 와 global Variable

 

사실 여기까지는 react 사용하시는 분들은 대부분 알고계시는 내용입니다. 그렇다면, global Variable의 경우는 어떨까요!?!?

(여기서의 global Variable은 컴퍼넌트 외부에 let 또는 const 로 선언하는 variable을 의미합니다.)

 

(내부에서 선언되는 variable 은 컴퍼넌트 리렌더링시마다 초기화됩니다. 이는 비교적 당연하게 받아들여지는 케이스기 때문에 자세한 설명은 생략할게요.)

 

저는 useRef 를 사용할때마다 궁금했던 점이, 렌더링에 관여하지 않는 변수면, 그냥 컴퍼넌트 외부에 let 으로 선언하는 변수와 무엇이 다른것일까? 하는 의문이 들곤 했습니다. 아니, 애초에 컴퍼넌트 외에 let 선언한 변수를 컴퍼넌트 내부에서 사용한적이 거의 없다보니, global Variable은 시도조차 해본적이 거의 없었고, 뭔가 금기시되는 것처럼 느껴지기도 했죠.

 

컴퍼넌트 안에서도, global Variable을 변화시킬 수 있습니다. 이는 컴퍼넌트 내부에서 선언한 let, const variable 과 달리, 리렌더링마다 초기화되지도 않죠. 이렇게 사용해도 문제가 일어나지는 않습니다. 

 

단, 컴퍼넌트를 1개만 사용할 때에만 말이죠!! ⚠️

 

리액트에서 컴퍼넌트는, 재사용이 가능하죠. global하게 let으로 선언한 변수는, 여러 컴퍼넌트를 사용하게 된다면 그 값을 모두 공유하게 됩니다. 컴퍼넌트가 사라졌다가 재등장하게 되더라도, global한 변수의 값은 유지될 것입니다. 물론 이 점을 잘 고려한다면 유용하게 사용할 수도 있습니다.(모든 컴퍼넌트가 intergrated 한 global한 변수를 바라보는 케이스)

 

useRef로 선언한 객체는 이와 달리, 컴퍼넌트와 생애주기를 함께합니다. 컴퍼넌트가 생성됨과 동시에 값이 초기화되고, 컴퍼넌트가 unMount 되면 메모리에서 해제될 것입니다.

요악해서 global Variable과 가장 큰 다른점은,  컴퍼넌트 재사용시마다 변수도 각 컴퍼넌트에 맞게 새롭게 초기화된다는 것을 의미합니다!

 

 

위 예시는  useState, useRef, global Variable 로 같은 로직을 가지는 세 variable을 만들어본 케이스입니다. 

refCount.current 값과 globalCount의 값은, 증가되더라도 화면에 바로 렌더링되지 않습니다. useState로 선언된 stateCount 가 변화될 때 일어나는 렌더링과 함께 변화된 값이 화면에 드러나게 되죠. 

 

앞선 예시와 다른 점은, 컴퍼넌트가 여러 개 재사용되고 있다는 점입니다. 이런 케이스에서, 컴퍼넌트 1컴퍼넌트 2 는 globalCount 값을 공유하게 됩니다. 컴퍼넌트 1의 plus 버튼이 눌려지면 컴퍼넌트 2 에서도 globalCount 값이 올라가게 되죠. 하지만 refCount 는 그렇지 않습니다. 컴퍼넌트 1  의 refCount 값 컴퍼넌트 2  의 refCount 값은 각각 다른 값이죠. 

 

직접 테스트해보시면서 파악하시면 이해가 더 빠를 것입니다!

 

복습 📖

 

지난 포스팅에서 제가 제시했던 문제입니다. 

이번 포스팅을 이해하셨다면, 이 문제를 더 쉽게 이해하실 수 있습니다.

 

const inputEl = { current: null };

function App() {
  const onButtonClick = () => {
    inputEl.current.focus();
    inputEl.current.click();
  };
  const onInputClick = () => {
    alert("input clicked");
  };

  console.log({ inputEl });

  return (
    <>
      <input onClick={onInputClick} ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

위 예시에서 버튼을 클릭했을 때, useRef 로 선언된 객체가 아닌 global 하게 직접 선언된 const inputEl = { current: null } 로도 이 ref={inputEl} 를 통한 onInputClick이 동작할까요!?

 

이제는 정답을 아시겠죠? 

당연히 동작합니다 !  😄

 

 

정답 직접 확인하기

 

 

 

하지만 이 방식은 좋지 않습니다. 이 App() 이라는 컴퍼넌트가 여러 번 재사용된다고 가정했을 시에, 하나의 변수 inputEl 가 여러 엘리먼트에 종속되게 되면서, inputEl.current 가 의도하지 않은 엘리먼트를 가리키게 될 수도 있겠죠. 이 예시는 useRef 가 { current : T } 형태의 객체를 선언한다는 것을 보여주기 위한 예시였을 뿐, 실제로 이 형태로 사용하는 것은 지양하시기 바랍니다

 

긴 글 읽어주셔서 갑사합니다.

 

 

참조

 

https://stackoverflow.com/questions/57444154/why-need-useref-and-not-mutable-variable

 

Why need useRef and not mutable variable?

I have read A Complete Guide to useEffect - Swimming Against the Tide at Overreacted. The example shows that if we want to get the latest count, we can use useRef to save the mutable variable, an...

stackoverflow.com

 

728x90

댓글