📌 useRef
: 컴포넌트의 정보를 기억하되, 해당 정보가 렌더링을 유발하지 않도록 하는 Hook
- 현재(current) 기억된 값이 변경되더라도 기억하지만, 리-렌더링을 요청하지 않음
- state와 달리 ref의 current 값을 설정하면 리렌더가 트리거되지 않는다❌ - 어떤 데이터를 기억하고 싶은데 리렌더링은 안 하고 싶을 때 사용
- 화면 업데이트 ❌ 👉 리렌더링을 해야 바뀜 - 리액트의 것(리액트 훅)이 아닌 것이 아닌 것을 기억해야 할 때 사용
- DOM 엘리먼트 저장 및 조작, JSX를 계산하는 데 필요하지 않은 다른 객체 저장 등에 사용
- 자바스크립트 순수 객체 반환
{ current: null } → DOM Mount → { current: HTMLElement }
- useState 👉 기억된 값을 재렌더링 시 가져온다.
- useRef 👉 값은 기억하되 렌더링을 유발하지 않는다.
⏩ 사용
1. useRef 불러오기
import { useRef } from 'react';
2. useRef Hook 사용
- 참조할 초기값을 유일한 인자로 전달
- ref.current 프로퍼티를 통해 해당 ref의 current 값에 접근할 수 있다.
- current 프로퍼티: ref가 참조하는 DOM 요소나 값을 저장 - useRef(null)인 이유
- element를 참조할때 기본적으로 null이 들어오기 때문
const ref = useRef(null);
// return { current: null }
✅ ref로 DOM 조작하기
- 실제 DOM에 렌더링 된 이후 HTML 요소에 접근 가능
- 리액트에서 렌더 트리 생성 ➡︎ 리액트 돔에 실제 DOM 생성 ➡︎ ref 콜백 함수 실행 (마운트 시점에서 실행) - 참조 콜백(ref callback)
👉 domElementNode(실제 DOM 요소)를 갖는다
const refCallback = (el /* domElementNode */) => {
console.log(el);
}
⏩ 사용
1. useRef 불러오기
import { useRef } from 'react';
2. useRef Hook 사용
const titleRef = useRef(null);
3. 참조 콜백(ref callback) 또는 이벤트 핸들러(event handler)에 사용
- 참조 콜백(ref callback)
👉 domElementNode(실제 DOM 요소)를 갖는다
// 참조 콜백
const refCallback = (el) => {
if (el) {
VanillaTilt.init(el, TILT_CONFIG);
}
};
// 이벤트 핸들러
const handleEnter = () => {
const title = titleRef.current;
title.style.opacity = '1';
};
4. (DOM 노드를 가져와야하는) JSX 태그에 ref 어트리뷰트로 전달
<div ref={refCallback}>
ex 1) Vanilla Tilt 이펙트(애니메이션) 적용하기
import VanillaTilt from 'vanilla-tilt';
const TILT_CONFIG = {
reverse: true,
max: 15,
startX: 0,
startY: 0,
perspective: 1000,
scale: 1.2,
speed: 600,
transition: true,
axis: null,
reset: true,
'reset-to-start': true,
easing: 'cubic-bezier(.03,.98,.52,.99)',
glare: false,
'max-glare': 0.65,
'glare-prerender': false,
'mouse-event-element': null,
gyroscope: true,
gyroscopeMinAngleX: -45,
gyroscopeMaxAngleX: 45,
gyroscopeMinAngleY: -45,
gyroscopeMaxAngleY: 45,
};
function CardLinkItem({ item, popup = false, external = false }) {
const { href, label, images = {} } = item;
const cardClassNames = `${S.card} ${popup ? S.popup : ''}`.trim();
let externalLinkProps = null;
if (external) {
externalLinkProps = {
target: '_blank',
rel: 'noreferrer noopener',
};
}
const titleRef = useRef(null); // { current: null }
// 참조 콜백(ref callback)
const refCallback = (el) => {
if (el) {
VanillaTilt.init(el, TILT_CONFIG);
}
};
// 이벤트 핸들러
// - 외부 시스템인 DOM 요소에 접근, 조작
const handleEnter = () => {
const title = titleRef.current;
title.style.opacity = '1';
};
const handleLeave = () => {
titleRef.current.style.opacity = '0';
};
return (
<a
// key, ref
key={'identity'}
ref={refCallback}
className={S.component}
aria-label={label}
title={label}
href={href}
onPointerEnter={handleEnter}
onPointerLeave={handleLeave}
{...externalLinkProps}
>
<figure className={cardClassNames}>
<div className={S.wrapper}>
<img className={S.coverImage} src={images.cover} alt="" />
</div>
<img ref={titleRef} className={S.title} src={images.title} alt="" />
<img className={S.character} src={images.character} alt="" />
</figure>
</a>
);
}
✅ forwardRef()
: 부모 컴포넌트에서 하위 컴포넌트의 DOM을 조작할 수 있도록 하위 컴포넌트에서 부모 컴포넌트의 ref를 받아온다.
const SomeComponent = forwardRef(render)
- 매개변수
- render: 컴포넌트의 렌더링 함수
(React는 컴포넌트가 부모로부터 받은 props와 ref로 이 함수를 호출한다.)
- render: 컴포넌트의 렌더링 함수
- 기본적으로 각 컴포넌트의 DOM 노드는 비공개이다.
➡ forwardRef()로 감싸면 부모에 DOM 노드를 노출할 수 있다⭕
- ref는 prop으로 전달할 수 없다.
➡ forwardRef() 사용
- forwardRef가 반환하는 컴포넌트는부모로부터 ref prop을 받을 수 있다⭕ - 고차 컴포넌트 사용
- prop types 사용 불가❌ (➡ TypeScript 사용)
- 상위 컴포넌트에 공유 불가❌
⏩ 사용
1. forwardRef 불러오기
import { forwardRef } from 'react';
2. ref를 받아서 사용할 렌더 컴포넌트 감싸기
- 해당 컴포넌트의 props으로 ref 전달 & 사용
- 컴포넌트가 부모로부터 받은 props와 ref로 해당 함수를 호출
const SomeComponent = forwardRef(function MyInput(props, ref) {
return (
// ...
)
});
3. 노출하려는 하위 컴포넌트의 DOM 노드에 ref 어트리뷰트 설정하기
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<input {...otherProps} ref={ref} />
)
});
➡ 부모 컴포넌트(Form)가 자식 컴포넌트(MyInput)의 <input> DOM 노드에 접근할 수 있다.
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
</form>
);
}
✅ useImperativeHandle()
: 하위 컴포넌트에서 ref로 노출할 이벤트 핸들러를 정의한다.
useImperativeHandle(ref, createHandle, dependencies?)
- 매개변수
- ref: forwardRef 렌더링 함수에서 두 번째 인자로 받은 ref
- createHandle: 노출하려는 ref 핸들을 반환하는 함수(노출하려는 메서드가 있는 객체를 반환)
- dependencies: createHandle 코드 내에서 참조하는 모든 반응형 값을 나열한 목록입
- 반응형 값: props, state 및 컴포넌트 내에서 직접 선언한 모든 변수와 함수
- 부모 컴포넌트가 자식 컴포넌트의 특정 메서드나 속성에 접근할 수 있다⭕
- 주로 forwardRef와 함께 사용
⏩ 사용
1. useImperativeHandle 불러오기
import { forwardRef, useImperativeHandle } from 'react';
2. forwardRef 함수 내부에 useImperativeHandle 사용
const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// 메서드
};
}, []);
}
➡ 부모 컴포넌트(Form)가 자식 컴포넌트(MyInput)의 focus 및 scrollIntoView 메서드를 호출할 수 있다.
(기본 <input> DOM 노드의 전체 엑세스 권한은 없음)
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
});
📢 함수 내에서 데이터 기억하는 방법
* 함수 내 지역 변수(함수 실행 이후엔 기억되지 않는다.)
1. useState()
- 메모이제이션, 외부에 데이터 기억 저장/읽기
- 필수적으로 리액트 컴포넌트를 리-렌더링 하도록 요청⭕
👉 값을 기억하기 위해 사용하는 것은 낭비 - 화면을 업데이트(변경) 해주는 데이터
👉 리액트의 것(리액트 훅)을 기억하고, 컴포넌트(함수)를 다시 실행시킬 때 사용 - Immutable(불변)
let [message, setMessage] = useState(''); // return [state, setState]
2. useRef()
- 메모이제이션, 외부에 데이터 기억 저장/읽기
- 현재(current) 기억된 값이 변경되더라도 기억은 하지만, 리-렌더링 하도록 요청하지 않음❌
- 어떤 데이터를 기억하고 싶은데 리렌더링은 안 하고 싶을 때
(화면 업데이트 X, 리렌더링을 해야 바뀜)
👉 리액트의 것(리액트 훅)이 아닌 것이 아닌 것을 기억해야 할 때 사용 - Mutable(변경 O)
const messageRef = useRef('');
// return { current: '' }
// 일반 js 객체가 반환, current에 초기값을 할당
☑️ useRef vs useState
useRef(initialValue)는 { current: initialValue } 반환 |
useState(initialValue)은 [value, setValue] 반환 |
state를 바꿔도 리렌더링❌ | state를 바꾸면 리렌더링⭕ |
Mutable -렌더링 프로세스 외부에서 current 값을 수정 및 업데이트할 수 있다. |
Immutable - state 를 수정하기 위해서는 state 설정 함수를 통해 리렌더 대기열에 넣어야 한다. |
렌더링 중에는 current 값 수정❌ | 언제든지 state를 읽을 수 있다⭕ 그러나 각 렌더마다 변경되지 않는 자체적인 state의 snapshot이 있다. |
Ref로 값 참조하기 – React
The library for web and native user interfaces
ko.react.dev
Ref로 DOM 조작하기 – React
The library for web and native user interfaces
ko.react.dev
공통 컴포넌트 (예시: <div>) – React
The library for web and native user interfaces
ko.react.dev
forwardRef – React
The library for web and native user interfaces
ko.react.dev
useImperativeHandle – React
The library for web and native user interfaces
ko.react.dev