ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 221220 TIL(useRef)
    하루 30분 TIL 2022. 12. 20. 21:20

    useRef를 어떨 때 쓸까? 

    0. useRef로 특정 DOM 선택

     - 특정 엘리먼트의 크기, 스크롤바 위치 혹은 포커를 설정해줘야 한다던지 DOM을 직접 선택해야 하는 상황

    - D3, chart.js 같은 외부 라이브러리 사용할 때에도 특정 DOM에다 적용하기 때문에 DOM을 선택해야 하는 상황 발생


    1. useRef를 활용한 리렌더링 방지 

    input에 이름을 입력한 후 제출 버튼 클릭 시 입력된 이름 상태가 문장에 추가되도록 코르를 작성한다고 하자.

    useState을 사용한다면 아래와 같이 사용할 것이다.

    import React, { useState } from "react"
    
    function App() {
      const [name, setName] = useState("")
      const [currentName, setCurrentName] = useState("")
    
      console.log("render")
    
      return (
        <>
          <input value={name} onChange={e => setName(e.target.value)} />
          <button onClick={() => setCurrentName(name)}>제출</button>
          <div>나의 이름은 {currentName} 입니다.</div>
        </>
      )
    }
    
    export default App

    이때 콘솔 로그로 렌더링을 확인했을 때 입력할 때마다 렌더링이 일어나는 것을 알 수 있다. 

    렌더링을 통한 가시성 변화를 주는 작업이 아닐 때 굳이 useState을 통한 렌더링을 할 필요가 없다. 

    이때 useRef를 활용하여 렌더링을 방지할 수 있다. 

    import React, { useState, useRef } from "react"
    
    function App() {
      const [currentName, setCurrentName] = useState("")
      const inputRef = useRef("")
    
      console.log("render")
    
      return (
        <>
          <input ref={inputRef} />
          <button onClick={() => setCurrentName(inputRef.current.value)}>제출</button>
          <div>나의 이름은 {currentName} 입니다.</div>
        </>
      )
    }
    
    export default App

    마지막 div인 currentName의 경우 가시성이 필요하기 때문에 useState을 활용해 상태를 관리하지만 

    input의 경우 굳이 렌더링이 필요없다. 

    

    2. 다시 렌더링 되어도 동일한 참조값을 유지하고 싶을 때 

    1번과 같은 맥락이긴 하지만  아래의 조건을 만족하는 코드를 짠다고 생각해보자

     

    "숫자가 mount될 때 0부터 차례로 증가, unmount될 때 현재 화면의 숫자를 alert 해준다."

    useState을 활용해 아래의 코드를 작성해보면 어떻게 될까? 

    import React, {useEffect, useState} from 'react';
    
    const Counter = () => {
      const [counter, setCounter] = useState(0);
    
      useEffect(() => {
        const timer = setInterval(() => {
          setCounter(prev => prev + 1);
        }, 1000);
        return () => {
          clearInterval(timer);
          alert(counter);
        };
      },[]);
    
      return (
        <div>
          <p>{counter}</p>
        </div>
      );
    };
    
    export default Counter;

    alert창에 띄어진 counter의 값은 항상 0을 바라보고 있는 것을 알 수 있다. 

    그 이유는 setInterval 안의 setCounter로 인해 1초마다 렌더링이 일어나며 useEffect안의 counter값도 초기값인 0을 항상 바라보게 된다. 

     

    이때 사용할 useState 대신 useRef를 사용할 수 있다.

    import React, {useEffect, useState, useRef} from 'react';
    
    const Counter = () => {
      const [counter, setCounter] = useState(0);
      const unmountvisual = useRef(0);
    
      useEffect(() => {
        const timer = setInterval(() => {
          setCounter(prev => prev + 1);
          unmountvisual.current += 1;
        }, 1000);
        return () => {
          clearInterval(timer);
          alert(unmountvisual.current);
        };
      },[]);
      
      return (
        <div>
          <p>{counter}</p>
        </div>
      );
    };
    
    export default Counter;

    위 코드로 작성 시 정상적으로 작동하는 것을 알 수 있다.

    그 이유는 useRef를 통해 반환된 객체는 component의 생애주기 내내 변화하는 값을 가리키고 있기 때문입니다. 

     

     

    참고

    https://react.vlpt.us/basic/10-useRef.html

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

    https://velog.io/@kysung95/%EC%A7%A4%EB%A7%89%EA%B8%80-useRef%EA%B0%80-%EB%AD%94%EA%B0%80%EC%9A%94

    '하루 30분 TIL' 카테고리의 다른 글

    230113 30분 TIL forEach의 return  (0) 2023.01.13
    2301112 30분 TIL (a태그 noopener noreferrer / SOLID)  (0) 2023.01.13
    221214 TIL(useImperativeHandler)  (0) 2022.12.14
    221208 TIL(uncontrolled vs controlled)  (0) 2022.12.09
    221103 TIL  (0) 2022.11.04

    댓글

Designed by Tistory.