2023.01.31 useRef
useRef는 레퍼런스(Reference)를 사용하기 위한 훅이다.
.current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다.
반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다.
변경은 관리해야 하지만 리렌더링을 발생 시키지 않아도 되는 값을 다룰 때 사용하기
위의 내용을 비교를 통해 이해해보기.
import { useState, useRef } from 'react'
const Component = () => {
const a = 1 // 일반 변수
const [state, setState] = useState() // state 변수
const ref = useRef() // ref 변수
}
컴포넌트의 생애주기란 DOM에 mount 되고 unmount 되기까지의 과정을 말한다.
함수 컴포넌트는 부모로 부터 전달 받는 props가 변경되거나 자신의 state가 변경되면 re-rendering 이 발생하는데,
이 때 내부에서 const 로 선언된 변수는 재선언되고 재할당 된다.
즉 컴포넌트의 생애주기를 통해 유지되지 않고 렌더링 마다 값이 초기화된다.
리액트에서 레퍼런스란 특정 컴포넌트에 접근할 수 있는 객체를 의미한다.
useRef( ) 훅은 바로 레퍼런스 객체를 반환한다.
레퍼런스 객체에는 .current라는 속성이 있는데 이것은 현재 레퍼런스(참조)하고 있는
엘리먼트를 의미한다고 보면된다.
const refContainer = useRef(초깃값);
useRef( ) 훅을 사용하면 파라미터로 들어온 초깃값으로 초기화된 레퍼런스 객체를 반환한다.
만약 초깃값이 null이라면 .current의 값이 null인 레퍼런스 객체가 봔환된다.
이렇게 반환된 레퍼런스 객체는 컴포넌트의 라이프타임 전체에 걸쳐서 유지된다.
즉, 컴포넌트가 마운트 해제 전까지는 계속 유지된다는 것이다.
쉽게 말해 useRef( )훅은 변경 가능한 .current라는 속성을 가진 하나의 상자이다.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 는 마운트된 input element를 가리킴
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
위 코드는 useRef( ) 훅을 사용하여 버튼 클릭 시 <input>에 포커스를 하도록 하는 코드이다.
초깃값으로 null을 넣었고 결과로 반환된 inputElem이라는 레퍼런스 객체를 <input> 태그에 넣어줬다.
그리고 버튼 클릭 시 호출되는 함수에서 inputElem.current를 통해 실제 엘리먼트에 접근하여 focus( ) 함수를 호출하고 있다.
이전에 웹사이트 개발을 해 본 경우 레퍼런스와 관련하여 DOM에 접근하는 방법으로 refs에 친숙할 지도 모르겠습니다.
비슷하게 리액트에서는
<div ref={myRef} />
를 사용하여 React로 ref 객체를 전달한다면,
React는 노드가 변경될 때마다 변경된 DOM 노드에 그것의 .current 프로퍼티를 설정할 것입니다.
그렇지만, ref 속성보다 useRef()가 더 유용합니다. 이 기능은 클래스에서 인스턴스 필드를 사용하는 방법과 유사한 어떤 가변값을 유지하는 데에 편리합니다.
이것은 useRef()가 순수 자바스크립트 객체를 생성하기 때문입니다. useRef()와 {current: ...} 객체 자체를 생성하는 것의 유일한 차이점이라면 useRef는 매번 렌더링을 할 때 동일한 ref 객체를 제공한다는 것입니다.
또한 useRef는 내용이 변경될 때 그것을 알려주지는 않는다는 것을 유념하세요.
.current 프로퍼티를 변형하는 것이 리렌더링을 발생시키지는 않습니다.
React가 DOM 노드에 ref를 attach(연결)하거나 detach(분리)할 때 어떤 코드를 실행하고 싶다면 대신 콜백 ref를 사용하세요.
DOM node의 변화를 어떻게 알 수 있을까?
(callback ref 사용하기)
DOM node의 변화를 알기 위한 가장 기초적인 방법으로 csllback ref를 사용하는 것이 있다.
리액트는 ref가 다른 node에 연결될 때마다 콜백을 호출하게 된다.
import { useCallback, useState } from "react";
function measureExaple(props) {
const [height, setHeight] = useState(0);
const measureRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientReact().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>안녕, 리액트</h1>
<h2>위 헤더 높이는 {Math.round(height)}px 입니다.</h2>
</>
);
}
위 코드에는 레퍼런스를 위해서 useRef( )훅을 사용하지 않고
useCallback( ) 훅을 이용한 callback ref 방식을 사용했다.
useRef( ) 훅을 사용하게 되면 레퍼런스 객체가 .current 속성이 변경되었는지를 따로 알려주지 않기 때문이다.
하지만 callback ref 방식을 사용하게 되면 자식 컴포넌트가 변경되었을 떄 알림을 받을 수 있고,
이를 통해 다른 정보들을 업데이트할 수 있다.
이 예제 코드에서는 <h1> 태그의 높이 값을 매번 업데이트 하고 있다.
그리고 usecallback( ) 훅의 의존성 배열로 비어있는 배열을 넣었는데,
이렇게 하면 <h1> 태그가 마운트,언마운트될 때만 콜백 함수가 호출되며 재렌더링이 일어날 때에는 호출되지 않는다.