Dmitri Pavlutin | React에서의 useRef(), Refs 완벽가이드 (번역)
원문
Dmitri Pavlutin | The Complete Guide to useRef() and Refs in React
The Complete Guide to useRef() and Refs in React
How to use React.useRef() hook to create persisted mutable values (also known as references or refs), as well access DOM elements.
dmitripavlutin.com
이 글을 통해 React.useRef()훅을 사용하여 지속되는 가변 값(refs 또는 참조references라고도 함)을 생성하고, DOM 요소에 접근하는 방법을 배웁니다.
목차
1. 가변 값
1.1 예시: 로그인 버튼 클릭
1.2 예시: 스탑워치 만들기
2.DOM요소에 접근하기
2.1 예시: input에 focus걸기
3. 참조 업데이트 제약
4. 요약
1. 가변 값
useRef(initialValue)는 하나의 인자를 초기 값으로 받아들이고 참조(일명 ref)를 반환하는 React의 내장 훅입니다 . 참조는 특별한 속성인 current를 가진 객체입니다.
import { useRef } from 'react';
function MyComponent() {
const reference = useRef(initialValue);
const someHandler = () => {
// Access reference value:
const value = reference.current;
// Update reference value:
reference.current = newValue;
};
// ...
}
reference.current은 참조 값에 접근하고 reference.current = newValue는 참조 값을 업데이트합니다. 아주 간단합니다.
객체: 참조
current = 참조값
참조에 대해 기억해야 할 두 가지 규칙이 있습니다.
- 참조 값은 컴포넌트를 다시 렌더링하는 사이에 유지됩니다(동일하게 유지됨) .
- 참조를 업데이트 해도 컴포넌트는 다시 렌더링되지 않습니다 .
useRef()이제 실전에서 어떻게 사용하는지 알아보겠습니다 .
1.1 예시: 로그인 버튼 클릭
LogButtonClicks컴포넌트는 참조를 사용하여 버튼 클릭 수를 저장합니다.
import { useRef } from 'react';
function LogButtonClicks() {
const countRef = useRef(0);
const handle = () => {
countRef.current++;
console.log(`Clicked ${countRef.current} times`);
};
console.log('I rendered!');
return <button onClick={handle}>Click me</button>;
}
const countRef = useRef(0)는 0으로 초기화된 참조 countRef를 생성합니다.
버튼을 클릭하면 handle함수가 호출되고 참조 값이 증가합니다: countRef.current++. 참조 값은 콘솔에 기록됩니다.
참조 값을 업데이트 countRef.current++해도 컴포넌트의 리-렌더링이 트리거되지 않습니다. 'I rendered!'이는 초기 렌더링 시 콘솔에 한 번만 기록됩니다. 참조가 업데이트될 때 다시 렌더링되지 않는다는 사실이 입증됩니다.
이제 합리적인 질문입니다. 참조(references)와 상태(state)의 주요 차이점은 무엇입니까?
참조 및 상태 차이
방금전의 컴포넌트를 재사용 LogButtonClicks하되 이번에는 useState()훅을 사용하여 버튼 클릭 수를 계산합니다.
import { useState } from 'react';
function LogButtonClicks() {
const [count, setCount] = useState(0);
const handle = () => {
const updatedCount = count + 1;
console.log(`Clicked ${updatedCount} times`);
setCount(updatedCount);
};
console.log('I rendered!');
return <button onClick={handle}>Click me</button>;
}
데모를 열고 버튼을 클릭해보세요. 클릭할 때마다 콘솔에 메시지가 표시됩니다. 'I rendered!'즉, 상태가 업데이트될 때마다 구성 요소가 다시 렌더링된다는 의미입니다.
따라서 참조와 상태의 두 가지 주요 차이점은 다음과 같습니다.
- 참조 업데이트는 다시 렌더링을 트리거하지 않지만 상태를 업데이트하면 컴포넌트가 다시 렌더링됩니다.
- 참조 업데이트는 동기적(업데이트된 참조 값을 즉시 사용할 수 있음)인 반면 상태 업데이트는 비동기적(상태 변수가 다시 렌더링된 후 업데이트됨)입니다.
더 크게보면 참조는 side-effects에 대한 인프라 데이터를 저장하고, 상태는 화면에 직접 렌더링되는 정보를 저장합니다.
1.2 예시: 스탑워치 만들기
side effects의 인프라 데이터를 참조 내부에 저장할 수 있습니다. 예를 들어 타이머 ID, 소켓 ID 등을 참조 포인터에 저장할 수 있습니다.
이 Stopwatch 컴포넌트는 setInterval(callback, time) 타이머 함수을 사용하여 스톱워치의 카운터를 매초마다 증가시킵니다. 타이머 ID는 참조의 timerIdRef에 저장됩니다.
import { useRef, useState, useEffect } from 'react';
function Stopwatch() {
const timerIdRef = useRef(0);
const [count, setCount] = useState(0);
const startHandler = () => {
if (timerIdRef.current) { return; }
timerIdRef.current = setInterval(() => setCount(c => c+1), 1000);
};
const stopHandler = () => {
clearInterval(timerIdRef.current);
timerIdRef.current = 0;
};
useEffect(() => {
return () => clearInterval(timerIdRef.current);
}, []);
return (
<div>
<div>Timer: {count}s</div>
<div>
<button onClick={startHandler}>Start</button>
<button onClick={stopHandler}>Stop</button>
</div>
</div>
);
}
2.DOM요소에 접근하기
useRef()훅의 또 다른 유용한 적용사례는 DOM 요소에 접근하는 것입니다. 이는 3단계로 수행됩니다.
- 요소에 접근하기 위한 참조를 정의합니다 const elementRef = useRef()
- ref요소의 속성에 참조를 할당합니다 : <div ref={elementRef}></div>
- 마운트 후에 elementRef.current이 DOM 요소를 가리킵니다.
import { useRef, useEffect } from 'react';
function AccessingElement() {
const elementRef = useRef();
useEffect(() => {
const divElement = elementRef.current;
console.log(divElement); // logs <div>I'm an element</div>
}, []);
return (
<div ref={elementRef}>
I'm an element
</div>
);
}
2.1 예시: input에 focus걸기
컴포넌트가 마운트될 때, 예를 들어, input 필드에 focus하려면 DOM 요소에 접근해야 합니다.
작동하게 하려면 input에 대한 참조를 생성하고, 태그의 속성 ref에 참조를 할당하고, 마운트한 후 특수 메서드인 element.focus()를 요소에 호출해야 합니다.
<InputFocus>구성 요소 의 가능한 구현은 다음과 같습니다 .
import { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
return (
<input
ref={inputRef}
type="text"
/>
);
}
const inputRef = useRef()는 입력 요소를 보유하기 위해 참조를 생성합니다.
inputRef 는 그 후 input 필드의 ref 속성에 할당됩니다 : <input ref={inputRef} type="text" />.
그런 다음 React는 마운트 후 inputRef.current를 입력 요소로 설정합니다. 이제 프로그래밍적으로 input에 focus를 설정할 수 있습니다 inputRef.current.focus().
Ref는 초기 렌더링에서 null입니다.
초기 렌더링 중에 DOM 요소를 보유해야 하는 참조는 비어 있습니다.
import { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef();
useEffect(() => {
// Logs `HTMLInputElement`
console.log(inputRef.current);
inputRef.current.focus();
}, []);
// Logs `undefined` during initial rendering
console.log(inputRef.current);
return <input ref={inputRef} type="text" />;
}
초기 렌더링 중에 React는 여전히 컴포넌트의 출력이 무엇인지 결정하고 있으므로 아직 생성된 DOM 구조가 없습니다. 이것이 초기 렌더링 중에 inputRef.current이 undefined로 평가되는 이유입니다.
useEffect(callback, []) 훅은 input 요소가 이미 DOM에 생성된 경우 마운트 직후 콜백을 호출합니다.
useEffect(callback, [])의 callback 함수는 DOM이 구성되었음을 보장하므로 inputRef.current에 접근하기에 적합한 위치 입니다.
3. 참조 업데이트 제약
함수형 컴포넌트의 함수 스코프는 출력을 계산하거나 훅를 호출해야 합니다.
그렇기 때문에 참조 업데이트(및 상태 업데이트)는 컴포넌트의 함수의 직접적인 스코프 내에서 수행되어서는 안 됩니다.
참조는 useEffect()내부의 콜백 또는 핸들러(이벤트 핸들러, 타이머 핸들러 등) 내부에서 업데이트되어야 합니다.
import { useRef, useEffect } from 'react';
function MyComponent({ prop }) {
const myRef = useRef(0);
useEffect(() => {
myRef.current++; // Good!
setTimeout(() => {
myRef.current++; // Good!
}, 1000);
}, []);
const handler = () => {
myRef.current++; // Good!
};
myRef.current++; // Bad!
if (prop) {
myRef.current++; // Bad!
}
return <button onClick={handler}>My button</button>;
}
4. 요약
useRef()훅은 참조를 생성합니다.
const reference = useRef(initialValue)를 초기값과 함께 호출하면 reference라는 특수 객체가 반환됩니다. 참조 객체에는 current 속성이 있습니다. 이 속성을 사용하여 참조 값reference.current를 읽거나 reference.current = newValue를 업데이트 할 수 있습니다 .
컴포넌트를가 다시 렌더링하는 사이에 참조 값은 영구적입니다.
상태 업데이트와 달리 참조 업데이트는 컴포넌트의 재렌더링을 트리거하지 않습니다.
참조는 DOM 요소에도 접근할 수 있습니다. 액세스하려는 요소의 ref 속성에 참조를 할당합니다 . <div ref={reference}>Element</div>— 요소는 reference.current 에서 사용할 수 있습니다 .
한 줄 생각
초기 렌더링 중에 React는 여전히 컴포넌트의 출력이 무엇인지 결정하고 있으므로 아직 생성된 DOM 구조가 없습니다.이것이 초기 렌더링 중에 inputRef.current이 undefined로 평가되는 이유입니다.
useEffect(callback, []) 훅은 input 요소가 이미 DOM에 생성된 경우 마운트 직후 콜백을 호출합니다.
useRef를 부를 때 useEffect안에 부르는 이유를 몰랐는데 useEffect를 사용하면 '마운팅 이후'에 callback을 호출하기 위해 DOM이 생성된 후 부를 수 있다는 점 때문에 그랬다는 걸 깨닫게 되었다.
또 useRef는 DOM을 조작하는 것이기 때문에 side effect라고 볼 수 있다. 이런 side effect를 다루는 것이 useEffect이기에 ref는 useEffect에 넣어서 사용해야겠다.