React로 개발 요구 사항을 충족할 수는 없는 경우가 있다. 아래와 같이 DOM 엘리먼트의 주소값을 활용해야 하는 경우 특히 그렇다.
React는 이런 예외적인 상황에서 useRef으로 DOM 노드, 엘리먼트, 그리고 리액트 컴포넌트 주소값을 참조할 수 있다. 아래 예시 코드처럼 작성하시면 주소값을 활용할 수 있다.
const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다.
return (
<div>
<input ref={주소값을_담는_그릇} type="text" />
{/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
{/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */}
{/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있습니다. */}
</div>
);
자바스크립트에서 특정 DOM을 선택해야 할 때에는 DOM Selector를 사용한다.(ex. document.querySelector
)
리액트에서는 render() 메서드에 의해 만들어지는 DOM에 접근하는 방식 으로 ref 를 제공한다. 예를 들어 배송지 정보를 입력 받아야 하는 결제 페이지를 만들 때, 사용자가 휴대번호와 같은 필수 정보를 입력하지 않고 결제하기 버튼을 눌렀다면 ref 를 사용하여 휴대번호 input 창에 focus 할 수 있다. 또는 특정 element 의 크기를 가져오거나 이 요소가 DOM 으로부터 얼마만큼 떨어져 있는지 스크롤의 위치를 구할 수도 있다.
이럴 때 리액트 함수형 컴포넌트에서는 React Hooks 중 하나인 useRef() 함수를 사용한다. (+ 클래스형 컴포넌트에서는 콜백함수를 사용하거나 React.createRef 함수를 사용한다고 한다)
아래 예시 코드를 참고해보자. enter
를 입력할 때, 입력하는 위치가 어디냐에 따라 세 가지 경우로 조건문을 나눠 적었다. 입력하는 위치가 첫번째 input 일때는 그 다음 input 박스(두번째 input박스)로 커서가 이동함을 알 수 있다(focus 메소드 사용)
https://codesandbox.io/s/patient-worker-3kzhd?from-embed (출처: 코드스테이츠)
import React, { useRef } from "react";
const Focus = () => {
const firstRef = useRef(null);
const secondRef = useRef(null);
const thirdRef = useRef(null);
const handleInput = (event) => {
console.log(event.key, event);
if (event.key === "Enter") {
if (event.target === firstRef.current) {
// 위의 console.log 결과값으로 event 객체를 뜯어보니 target이라는 속성값이 <input></input> 으로 나온다...!
// 그리고 firstRef도 콘솔에 찍어보니 {current: <input></input>}라는 객체 타입으로 나온다.
secondRef.current.focus();
event.target.value = "";
} else if (event.target === secondRef.current) {
thirdRef.current.focus();
event.target.value = "";
} else if (event.target === thirdRef.current) {
firstRef.current.focus();
event.target.value = "";
} else {
return;
}
}
};
return (
<div>
<h1>타자연습</h1>
<h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
<div>
<label>hello </label>
<input ref={firstRef} onKeyUp={handleInput} />
</div>
<div>
<label>world </label>
<input ref={secondRef} onKeyUp={handleInput} />
</div>
<div>
<label>codestates </label>
<input ref={thirdRef} onKeyUp={handleInput} />
</div>
</div>
);
};
export default Focus;
일단! const 로 선언한 변수
/ useState() hook 으로 생성한 변수
/ useRef() hook
으로 생성한 변수 의 차이점을 알아보자.
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 이 발생한다.
https://codesandbox.io/s/wonderful-joliot-2q28x?fontsize=14&hidenavigation=1&theme=dark (출처: 이화랑블로그)
hello 버튼을 눌렀을 때는 즉시 리렌더링이 일어나지만 집사야 눌러봐 버튼을 눌렀을 때는 리렌더링이 발생하지 않고 내부적으로 ref.current 값은 계속 업데이트 되긴 되다가, 외부적인 이유(ex. state 변경)로 컴포넌트가 리렌더링 되면 그 때 변경된 값이 보여질 뿐이다.
이러한 특성으로 useRef() 로 생성한 ref.current 에 HTMLElement 뿐만 아니라 숫자, 문자열, 배열 등의 값을 할당 할 수 있으며, 컴포넌트 내부에서 변경을 관리해야 하지만 굳이 리렌더링을 발생 시킬 필요는 없을 때 활용할 수 있다.
useMemo와 비슷한 Hook.
useMemo 는 특정 결과값을 재사용 할 때 사용하는 반면, useCallback 은 특정 함수를 새로 만들지 않고 재사용하고 싶을때 사용한다.
(업데이트 예정)
(업데이트 예정)
참고자료
https://xiubindev.tistory.com/98
https://leehwarang.github.io/docs/tech/2020-11-29-ref.html
https://www.youtube.com/watch?v=t2ypzz6gJm0 (useRef)
https://www.youtube.com/watch?v=_AyFP5s69N4
(useCallback)
https://www.daleseo.com/react-hooks-use-callback/ (useCallback)