[React] ๋ฆฌ์กํธ useRef ํ์ฉ ์ผ์ด์ค ๋ชจ์
useRef
ํจ์๋ current
์์ฑ์ ๊ฐ์ง ref
๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ref.current
๊ฐ์ HTMLElement
์ ํ ๋นํด์ ํด๋น ์์์ focus
ํ๊ฑฐ๋, ์๋ฆฌ๋จผํธ ํฌ๊ธฐ ๋ฑ์ ํ์ธํ ์ ์๋ค. ref
๊ฐ์ฒด๋ ์๋ ํน์ง์ ๊ฐ๋๋ค.
- ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง ๋ผ๋
ref.current
๊ฐ์ ๊ทธ๋๋ก ์ ์งํ๋ค. ref.current
๊ฐ์ด ๋ณ๊ฒฝ๋ผ๋ ์ปดํฌ๋ํธ๋ ๋ค์ ๋ ๋๋งํ์ง ์๋๋คHTMLElement
๋ฟ๋ง ์๋๋ผ ์ซ์ / ๋ฌธ์์ด / ๋ฐฐ์ด ๋ฑ ๊ฐ์ ํ ๋นํ ์ ์๋ค.
Case 1 — ๋ฆฌ๋ ๋๋ง์ด ํ์ ์๋ ๊ฐ์ ๊ด๋ฆฌํ ๋ โญ๏ธ
useRef
๋.current
ํ๋กํผํฐ๋ก ์ ๋ฌ๋ ์ธ์(initialValue)๋ก ์ด๊ธฐํ๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํref
๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ๋ฐํ๋ ๊ฐ์ฒด๋ ์ปดํฌ๋ํธ์ ์ ์์ ์ฃผ๊ธฐ๋ฅผ ํตํด ์ ์ง๋ ๊ฒ์ ๋๋ค.
— React ๊ณต์ ๋ฌธ์
๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด useRef
ํจ์๊ฐ ๋ฐํํ ref
๊ฐ์ฒด๋ ์ปดํฌ๋ํธ ์์ ์ฃผ๊ธฐ๋ฅผ ํตํด ์ ์ง๋๋ค๊ณ ๋งํ๋ค. ์ฌ๊ธฐ์ ๋งํ๋ ์์ ์ฃผ๊ธฐ๋ DOM์ ๋ง์ดํธ~์ธ๋ง์ดํธ๊น์ง ๊ณผ์ ์ด๋ค. ์ปดํฌ๋ํธ๋ ๋ณ๊ฒฝ๋ props๋ฅผ ๋ฐ๊ฑฐ๋, ์์ ์ state๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ค์ ๋ ๋๋งํ๋๋ฐ, ์ด๋ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ์คํ๋๋ฉด์ const a = 1
๊ฐ์ ๋ณ์๋ ์ฌ์ ์ธ/์ฌํ ๋น ๋๋ค.
const a = 1; // ๋ ๋๋งํ ๋๋ง๋ค ๊ฐ ์ด๊ธฐํ
const [state, setState] = useState(1); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ ์ ๋ฆฌ๋ ๋๋ง
const ref = useRef(1); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง ์ํจ
์๋ 2๊ฐ ๋ฒํผ์ด ์๋ค. ์๋จ ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค useState
๋ก ์์ฑํ state
๊ฐ์ 1
์ฉ ๋ํ๊ณ , ํ๋จ ๋ฒํผ๋ ref.current
๊ฐ์ 1
์ฉ ๋ํ๋ค.
์๋จ ๋ฒํผ์ ํด๋ฆญํด์ state
๊ฐ์ด ๋ณํ๋ฉด ๋ฆฌ๋ ๋๋ง ๋ผ์ ํ๋ฉด์ ๋ฐ๋ก ์
๋ฐ์ดํธ ๋๋ค. ๋ฐ๋ฉด ํ๋จ ๋ฒํผ์ ํด๋ฆญํด์ ref.current
๊ฐ์ด ๋ณํด๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์์ผ๋ฏ๋ก ํ๋ฉด์๋ ์
๋ฐ์ดํธ๋์ง ์๋๋ค. ๋์ ๋ณํ ๊ฐ์ ๊ณ์ ์ ์งํ๊ณ ์๋ค.
ํ๋จ ๋ฒํผ์ 3๋ฒ ๋๋ฅด๋ฉด ref.current
๊ฐ +3
๋ผ์ ๋ด๋ถ์ ์ผ๋ก 4
๊ฐ์ ๊ฐ๊ณ (ํ๋ฉด์ ์
๋ฐ์ดํธ๋์ง ์์), ๋ค์ ์๋จ ๋ฒํผ์ ๋๋ฌ state
๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ฌ ๊ทธ์ ์์ผ ๋ณ๊ฒฝ๋ ref.current
๊ฐ์ ๋ณผ ์ ์๋ค.
import React, { useRef, useState } from "react";
const initialState = 1;
const buttonStyle =
"w-52 bg-blue-600 text-white p-2 rounded leading-none flex items-center justify-between active:bg-violet-500 focus:ring";
const spanStyle =
"font-bold bg-white p-1 rounded text-blue-600 text-xs ml-2 select-none";
const UseRef = function () {
const [state, setState] = useState(initialState); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ ์ ๋ฆฌ๋ ๋๋ง
const ref = useRef(initialState); // ๊ฐ ์ ์ง, ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง ์ํจ
return (
<div className="h-full flex flex-col justify-center items-center gap-2 text-lg text-neutral-700">
<button
className={buttonStyle}
type="button"
onClick={() => setState((prev) => prev + 1)}
>
STATE + 1 <span className={spanStyle}>{state}</span>
</button>
<button
className={buttonStyle}
type="button"
onClick={() => {
ref.current += 1;
console.log("ref.current: ", ref.current);
}}
>
REF.CURRENT + 1 <span className={spanStyle}>{ref.current}</span>
</button>
</div>
);
};
export default UseRef;
์ด ๊ฐ์ ref
๊ฐ์ฒด ํน์ฑ์ ์ด์ฉํด ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ณ๊ฒฝ๋ ๊ฐ์ ๊ด๋ฆฌํด์ผ ํ์ง๋ง, ํ๋ฉด์ ์
๋ฐ์ดํธ(๋ฆฌ๋ ๋๋ง) ํ ํ์ ์๋ ๊ฐ์ ๊ด๋ฆฌํ ๋ ์ ์ฉํ๊ฒ ์ฐ์ธ๋ค.
Case 2 — callbackRef (DOM Node ํฌ๊ธฐ ๊ตฌํ๊ธฐ) โญ๏ธ
useRef
๋ ๋ด์ฉ์ด ๋ณ๊ฒฝ๋ ๋ ๊ทธ๊ฒ์ ์๋ ค์ฃผ์ง ์๋๋ค๋ ๊ฒ์ ์ ๋ ํ์ธ์..current
ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋ผ๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์์ต๋๋ค. ๋ง์ฝ React๊ฐ DOM ๋ ธ๋์ ref๋ฅผ attachํ๊ฑฐ๋ detach ํ ๋ ์ด๋ค ์ฝ๋๋ฅผ ์คํํ๊ณ ์ถ๋ค๋ฉด ์ฝ๋ฐฑref
๋ฅผ ์ฌ์ฉํ์ธ์.
— React ๊ณต์ ๋ฌธ์(์ ๋ฌธ๋งฅ ์กฐ๊ธ ์์ )
ref.current
๊ฐ์ ๋ณ๊ฒฝํด๋ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์ง ์๋๋ค. ์ฆ, DOM ๋
ธ๋์ ref
์์ฑ์ ref.current
๊ฐ์ ํ ๋นํ๊ฑฐ๋(attach) ํ ๋น์ ํด์ ํด๋(detach) ์ปดํฌ๋ํธ๋ ์ด๋ฅผ ์ธ์งํ์ง ๋ชปํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
์๋ ์ฝ๋์์ wrapperRef.current
๊ฐ์ด null
์์ HTMLDivElement
๋ก ๋ณ๊ฒฝ๋ผ๋ ์ปดํฌ๋ํธ๋ ์ด๋ฅผ ์ธ์งํ์ง ๋ชปํ๋ค. ๋๋ฌธ์ console.log(wrapperRef.current)
๋ฅผ ์ฐ์ด๋ด๋ null
๋ก ๋์จ๋ค.
import React, { useRef } from "react";
const App = function () {
const wrapperRef = useRef(null);
console.log(wrapperRef.current); // null
return <div ref={wrapperRef}></div>;
};
ref
๊ฐ์ฒด๊ฐ DOM Node์ attach ํน์ detach๋ ๋ ํน์ ์์
์ ์ํํ๊ณ ์ถ๋ค๋ฉด callback ref ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ๋๋ค. DOM Node์ ref
์์ฑ์ ref
๊ฐ์ฒด๊ฐ ์๋ ํจ์๋ฅผ ๋๊ธฐ๋ ๋ฐฉ์์ด๋ค. DOM Node์ ๊ฐ๋ก/์ธ๋ก ๊ฐ์ ์์์ผ ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
๐ก node.offsetWidth
, node.offsetHeight
๊ฐ์ getBoundingClientRect
๋ฉ์๋๋ก ์กฐํํ width
, height
๊ฐ๊ณผ ๋์ผํ๋ค. ๋จ, offsetWidth
, offsetHeight
๋ ์์์ ์ ์ ์๋ก ๋ฐ์ฌ๋ฆผํ ๊ฐ์ ๋ฐํํ๋ค. (์ฐธ๊ณ ๋งํฌ)
import React, { useCallback, useState } from "react";
import CatImg from "../../assets/cat.png";
const CallbackRef = function () {
const [size, setSize] = useState({ height: 0, width: 0 });
const [loading, setLoading] = useState(true);
const callbackRef = useCallback(
(node) => {
if (node && !loading) {
const { width, height } = node.getBoundingClientRect();
setSize({ width, height });
}
},
[loading],
);
return (
<div className="h-full flex flex-col justify-center items-center gap-8">
<p className="text-lg text-neutral-700">
HEIGHT : {size.height} / WIDTH : {size.width}
</p>
<div ref={callbackRef}>
<img
className="max-w-[13rem]"
src={CatImg} // src={require(../assets/cat.png)}
alt="cat"
onLoad={() => setLoading(false)}
/>
</div>
</div>
);
};
export default CallbackRef;
// useCallback์ ์ฌ์ฉํ ์์
const callbackRef = useCallback((node) => {
if (node) {
/* ... */
}
}, []);
return (
<div ref={callbackRef}>
<img />
</div>
);
// useState๋ฅผ ์ฌ์ฉํ ์์
const [callbackRef, setCallbackRef] = useState(null);
useEffect(() => {
if (callbackRef) {
/* ... */
}
}, [callbackRef]);
return (
<div ref={callbackRef}>
<img />
</div>
);
Case 3 — forwardRef
props
๋ฅผ ์ ๋ฌํ๋ฏ ์์ ์ปดํฌ๋ํธ์๊ฒ ref
๋ฅผ ์ ๋ฌํ ์ ์๋ค. ๋ค๋ง ref
๋ props
๊ฐ์ฒด์ ํ๋กํผํฐ๋ก ๋ค์ด๊ฐ์ง ์๊ณ , ref
๋ฅผ ๋๊ฒจ ๋ฐ๋ ํ์ ์ปดํฌ๋ํธ๋ฅผ forwardRef
ํจ์๋ก ๊ฐ์ธ์ฃผ๋ ์์
์ด ํ์ํ๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ์ ref
์ํ๊ฐ์ ๊ด๋ฆฌํด์ผํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
import React from "react";
import { RandomCornerStyle, RandomWidthHeight } from "../../utils/webUtils";
const randomAxis = RandomWidthHeight(300, 120);
const randomCorner = RandomCornerStyle();
const Circle = React.forwardRef((props, ref) => {
return (
<div
className={`bg-indigo-500 ${randomCorner}`}
style={{
width: randomAxis.width,
height: randomAxis.height,
}}
ref={ref}
/>
);
});
Circle.displayName = "Circle"; // ESLint: react/display-name ์๋ฌ ๋์
const ForwardRef = function () {
const circleRef = React.useRef(null);
const [circleSize, setCircleSize] = React.useState({ width: 0, height: 0 });
React.useEffect(() => {
const { width, height } = circleRef.current.getBoundingClientRect();
setCircleSize({ width, height });
}, []);
return (
<div className="h-full flex flex-col justify-center items-center gap-8">
<p className="text-lg text-neutral-700">
WIDTH : {circleSize.width} / HEIGHT : {circleSize.height}
</p>
<Circle ref={circleRef} />
</div>
);
};
export default ForwardRef;
// ์์ ์ปดํฌ๋ํธ
const randomAxis = RandomWidthHeight(300, 120); // parameter: max, min
const Circle = React.forwardRef((props, ref) => {
return (
<div
style={{
width: randomAxis.width,
height: randomAxis.height,
}}
ref={ref}
/>
);
});
Circle.displayName = "Circle"; // ESLint: react/display-name ์๋ฌ ๋์
// ๋ถ๋ชจ ์ปดํฌ๋ํธ
const App = function () {
const circleRef = React.useRef(null);
// ...
return <Circle ref={circleRef} />;
};
์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก <Circle />
์ปดํฌ๋ํธ๋ ๋ ๋๋งํ ๋๋ง๋ค ๋๋คํ width
, height
๊ฐ์ ๊ฐ๋๋ค๊ณ ๊ฐ์ ํด๋ณธ๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ ์์ ์ปดํฌ๋ํธ์ ๋๋คํ width
, height
๊ฐ์ ์์์ผ ํ๋ ์ํฉ์ด๋ค.
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์์
ref
๋ฅผ ๋๊ธธ ์์ ์ ์์ง ์์ ์ปดํฌ๋ํธ์width
,height
๊ฐ์ ๋ชจ๋ฅด๋ ์ํ๋ค. - ์์ ์ปดํฌ๋ํธ๊ฐ ๋๋คํ
width
,height
๋ฅผ ๊ฐ์ง๊ณ , ์ ๋ฌ๋ฐ์ref
๋ฅผ attachํด์ ๋ ๋๋งํ๋ฉด, - ๋ง์ดํธ๋ฅผ ์๋ฃํ์ผ๋ฏ๋ก ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์๋
useEffect
๊ฐ ์คํ๋๋ค.useEffect
๊ฐ ์คํ๋ ์์ ์ ์์ ์ปดํฌ๋ํธ๋ก ๋๊ฒผ๋ref
๊ฐ์ฒด(circleRef
)๋ฅผ ํตํด,width
,height
๊ฐ์ ์ฝ์ ์ ์๋ค.
// ๋ถ๋ชจ ์ปดํฌ๋ํธ
const [circleSize, setCircleSize] = React.useState({ width: 0, height: 0 });
const circleRef = React.useRef(null); // null | HTMLDivElement
React.useEffect(() => {
const { width, height } = circleRef.current.getBoundingClientRect();
setCircleSize({ width, height });
}, []);
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ (0) | 2024.05.03 |
---|---|
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์ (0) | 2024.05.03 |
[HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ (0) | 2024.05.03 |
[HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ) (0) | 2024.05.03 |
[React] ๋ฆฌ์กํธ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ ๋ก๋) ๊ตฌํ / File API (0) | 2024.05.02 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
[HTML/CSS] ์์ดํฐ ์ฌํ๋ฆฌ ํ ๋ง ์ปฌ๋ฌ ๋ณ๊ฒฝ ๋ฐฉ๋ฒ
2024.05.03 -
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์
[HTML/CSS] ์ ์ฉํ HTML ํ๊ทธ ๋ชจ์
2024.05.03 -
[HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ
[HTML/CSS] CSS ์ธ์ ํ๋ ์๋ฆฌ๋จผํธ๋ค์ border ๊ฒน์นจ ๋ฌธ์ ํด๊ฒฐ
2024.05.03 -
[HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ)
[HTML/CSS] ์ด๋ฉ์ผ ํ ํ๋ฆฟ ์์ฑ๋ฒ (feat. ํ ์ด๋ธ ์ฝ๋ฉ)
2024.05.03