useRef

useRef는 왜? 쓰는걸까? 컴포넌트 내부 변수관리란??

함수형 컴포넌트에서 useRef 를 부르면 ref 오브젝트를 반환해준다

const ref = useRef(value)

ref 오브젝트는

  • {current : value}
  • 수정이 가능해서 언제든 원하는 값으로 바꿔줄 수 있다.
  • 컴포넌트 전 생애주기동안 값을 유지

언제쓸까?

저장공간으로 사용!

  • State 의 변화 -> 렌더링 -> 컴포넌트 내부 변수들 초기화
  • Ref 의 변화 -> No 렌더링 -> 변수들의 값이 유지됨
    state 대신 ref 를 사용하면 불필요한 렌더링을 줄일 수 있음
  • State 의 변화 -> 렌더링 -> 그래도 Ref 의 값은 유지됨
    컴포넌트가 아무리 변화돼도 ref 값이 유지되기 때문에 변경시 렌더링을 발생시키지 말아야 하는 값을 다룰때 편리!

DOM 요소에 접근
로그인화면이 보여질때 아이디를 넣는 input 을 굳이 클릭하지 않아도
자동으로 focus 를 되어있게 해주면 바로 키보드를 사용해서 아이디를 입력할 수 있기 때문에 짱편리!

useRef 사용해보기

이 코드는 함수형 컴포넌트 , state up 버튼을 누를때마다 렌더링이 된다!

import { useState } from "react";

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

  console.log('렌더링')

  const increaseCountState = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>State: {count}</p>
      <button onClick={increaseCountState}>State up</button>
    </div>
  );
}

export default App;

useRef 로 매번 렌더링되는 현상을 막아줄 수 있다.

일단 먼저 imort 에 useRef 를 불러오고

import { useState, useRef } from "react";

countRef 를 선언해준다.

  const countRef = useRef(0);

이 countRef 가 어떻게 동작하는지 console.log 로 찍어보니

이렇게 불려오는 것을 볼 수 있다.
보다시피 ref 는 하나의 object 이다!
그리고 ref 안에는 current 값, 내가 설정해준 초기값 0 이 들어있다.
ref 안에 있는 값에 접근하고 싶으면
countRef.current 로 출력해주면 된다.

버튼을 하나 더 만들어 줘보자 !

const increaseCountRef= () => {
    countRef.current = countRef.current + 1;
  }

 <button onClick={increaseCountRef}>Ref up</button>

버튼을 만들고 click 했을 때 increaseCountRef 함수가 동작하게 만들어 줬다.
increaseCountRef 함수는 countRef 의 current 값이 +1 씩 증가하는 함수이다.

이제 화면에는
이렇게 두개의 버튼이 있는데 ,

state up 버튼을 누를때마다 콘솔창에 렌더링이 찍히는 것을 볼 수가 있다.
state 는 한 번 실행될 때 마다 계속 App() 이 재렌더링 되기 때문이다!

자 이번에는 Ref up 버튼을 눌러봤는데 , 아무일도 일어나지 않는다
왜!?!?
Ref 는 아무리 수정해도 이 App 컴포넌트 전체를 다시 렌더링 시키지 않는다.
App 컴포안의 countRef 함수가 계속 증가하고 있는것은 맞음!
근데 렌더링이 되지 않고 있기 때문에 화면이 업데이트 되지 않는 것 .
여기서 이제 state up 버튼을 누르면 화면이 업데이트 되기 때문에
그동안 업데이트 되지 않았지만 클릭됨으로써 +1 되었던 Ref 값까지 업데이트 된다.

최종코드

import { useState, useRef } from "react";

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

  console.log(countRef);

  console.log("렌더링");

  const increaseCountState = () => {
    setCount(count + 1);
  };

  const increaseCountRef = () => {
    countRef.current = countRef.current + 1;
    console.log("Ref:", countRef.current);
  };

  return (
    <div>
      <p>State: {count}</p>
      <p>Ref : {countRef.current}</p>
      <button onClick={increaseCountState}>State up</button>
      <button onClick={increaseCountRef}>Ref up</button>
    </div>
  );
}

export default App;

변수와 차이

컴포넌트의 가장 자주 바뀌는 값을 state 에 넣는다면 성능에 안좋을텐데
이때 state 대신 useRef 를 넣으면 렌더링 되지 않아서 state 보다 성능에 영향주지 않을것!

import { useRef } from "react";

function App() {
  const [renderer, setRenderer] = useState(0);
  const countRef = useRef(0);
  let countVar = 0;

  const increaseRef = () => {
    countRef.current += 1;
    console.log("ref", countRef.current);
  };

  const increaseVar = () => {
    countVar += 1;
    console.log("var", countVar);
  };

  return (
    <div>
      <p>Ref : {countRef.current}</p>
      <p>Var : {countVar}</p>
      <button onClick={increaseRef}>Ref up</button>
      <button onClick={increaseVar}>Var up</button>
    </div>
  );
}

export default App;

이 코드를 실행시켰을 때 Ref 와 Var 렌더링 되지 않는다.
그래서 이 업데이트 되지 않는 아이들을 렌더링 해주기 위해서

const doRendering = () => {
    setRenderer(renderer + 1);
  };

doRendering 함수를 실행하는 버튼을 만들어주고
ref 와 var 이 렌더링되게 해주었다!
근데 왜 Ref 만 렌더링되고 Var 은 0 으로 되어있을까?

컴포넌트가 렌더링 된다는 것은 컴포넌트를 나타내는 함수가 다시 불린다는 것
즉 초기화가 된다는 것이다! 그래서 var은 countVar = 0 으로 초기화가 됨.

근데 ref 는 다르다. 아무리 컴포넌트가 렌더링 되더라도 ref 의 값을 유지한다!!
mounting 된 시점부터 해제된 시점까지 계속 제 값을 가지고 있음 .


ref 는 렌더링 이후에도 이전의 값부터 시작하는 것을 볼 수 있다.
하지만 var 은 값이 초기화되어 처음부터 시작한다.

더 확실한 비교를 위해 Ref Var 을 출력하는 함수를 만들어 보았다.
Ref up 버튼을 누르고, Var up 버튼을 누른 후 Ref var 을 클릭해주니,
ref:2 var:2 가 나왔다. 그리고 나서 Render 로 렌더링을 해주고 다시, Ref var 버튼을 클릭해주니, ref 는 초기화되지 않고 그대로, 변수는 0 으로 초기화가 되었다. 이게 ref와 그냥 변수의 차이이다.

최종코드

import { useRef } from "react";

function App() {
  const [renderer, setRenderer] = useState(0);
  const countRef = useRef(0);
  let countVar = 0;

  const doRendering = () => {
    setRenderer(renderer + 1);
  };

  const increaseRef = () => {
    countRef.current += 1;
    console.log("ref", countRef.current);
  };

  const increaseVar = () => {
    countVar += 1;
    console.log("var", countVar);
  };

  // 현재 ref 안에 있는 값과 변수안에 있는 값을 출력하는 함수
  const printResults = () => {
    console.log(`ref: ${countRef.current} var : ${countVar}`);
  };

  return (
    <div>
      <p>Ref : {countRef.current}</p>
      <p>Var : {countVar}</p>
      <button onClick={doRendering}>Render!</button>
      <button onClick={increaseRef}>Ref up</button>
      <button onClick={increaseVar}>Var up</button>
      <button onClick={printResults}>Ref var</button>
    </div>
  );
}

export default App;

무한굴레에 빠지지 않는 ref

useEffect 를 사용해서 up 을 눌렀을 때 count 가 올라갔으면 좋겠어.
라고 하면 보통 useState 를 만들어서 할텐데,
그렇게 하면 무한루프에 빠진다.

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

function App() {
  const [count, setCount] = useState(1);
  const [renderCount, setRenderCount] = useState(1);

  useEffect(() => {
    console.log('렌더링')
    setRenderCount(renderCount+1);
  });

  return (
    <div>
      <p>Count : {count}</p>
      <button onClick={() => setCount(count + 1)}>up </button>
    </div>
  );
}

export default App;


악! !! 무한루프에 갇혀서 무한 렌더링 중 ....
왜 이러는걸까여?
up 버튼을 클릭하면 countState 가 업데이트 되어 useEffect 가 불릴것 .
근데 이 useEffect 안에도 renderCount state를 업데이트 하는 코드가 있다.
이렇게 되면
Count state 가 불렸고, useEffect 가 업데이트 됐고, renderCount 도 업데이크 됐고..? 그럼 또 useEffect 가 불리고? 또 enderCount 도 업데이크 됐고..? 그럼 또 useEffect 가 불리고? 이게 무한 반복이 되는거임.....^^

이럴때 사용하는게 ref!!!

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

function App() {
  const [count, setCount] = useState(1);
  // const [renderCount, setRenderCount] = useState(1);
  const renderCount = useRef(1);

  useEffect(() => {
    renderCount.current++;
    console.log("렌더링수:", renderCount.current);
  });

  return (
    <div>
      <p>Count : {count}</p>
      <button onClick={() => setCount(count + 1)}>up </button>
    </div>
  );
}

export default App;

이렇게 무한루프에 빠지지 않고 버튼 클릭만큼 깔끔하게 렌더링이 되는 것을 볼 수 있음

DOM 접근하기

focus

input 을 클릭하지 않아도 focus 되게 만들어주려면
useRef 가 찰떡 !

  • DOM 요소에 직접적으로 접근해야 하는데 이걸 useref 를 통해 가능하게 할 수 있다.
    여기에서는 undefined 대신에 input 으로 넣어주는 값을 넣어줄 것 이다.

간단하다! input 안에 ref={들어갈 값} 을 작성해주고

 <input ref={inputRef} type="text" placeholder="아이디를 입력하세요" />

렌더링될때 처음에만 업데이트 되는 useEffrect,[] 를 써서
inputRef.current.focus(); 을 사용해주면
페이지에 들어왔을 때 바로 input 창이 focus 됨!

useEffect(() => {
    inputRef.current.focus();
  }, []);

여기서 더해서
아이디를 입력하고 로그인 버튼을 눌렀을 때 alert 가 뜨기 + alert 창을 닫았을 때 자동으로 다시 input 창이 focus 되게 만들어보자

 <button onClick={login}>로그인</button>

버튼을 클릭했을때 {login} 이 실행

const login = () => {
    alert(`welcome ${inputRef.current.value}!`);
    inputRef.current.focus();
  };

이 login 함수는 버튼을 틀릭했을때 alert창을 띄우고,

그 다음 다시 inputRef 를 focus 하게 만든다.

최종코드

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

function App() {
  const inputRef = useRef();

  useEffect(() => {
    // console.log(inputRef);
    inputRef.current.focus();
  }, []);

  const login = () => {
    alert(`welcome ${inputRef.current.value}!`);
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="아이디를 입력하세요" />
      <button onClick={login}>로그인</button>
    </div>
  );
}

export default App;
profile
Front-End Developer ✨

0개의 댓글

Powered by GraphCDN, the GraphQL CDN