home

useRef vs useState

๊ธ€ ๋ถ„๋ฅ˜
sub
ํ‚ค์›Œ๋“œ
react
์ƒ์„ฑ์ผ
2023/03/15 03:49
์ตœ๊ทผ ์ˆ˜์ •์ผ
2023/11/01 23:50
์ž‘์„ฑ์ค‘

TL;DR

๋ฆฌ์•กํŠธ์—์„œ ๋ Œ๋”๋ง ๋˜์–ด์•ผํ• , ๋ Œ๋”๋ง ์‹œ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฐ’๋“ค์„ ์‚ฌ์šฉํ•˜๋Š” escape hatch

๊ธฐ๋ณธ

const ref = useRef(0); console.log(ref) // { current:0 }
TypeScript
๋ณต์‚ฌ
โ€ข
๋ Œ๋”๋ง์„ ์œ ๋ฐœ ์‹œํ‚ค๊ณ  ์‹ถ์ง€๋Š” ์•Š๊ณ  ๊ทผ๋ฐ ๋ Œ๋”๋ง๊ฐ„ ์ •๋ณด๋Š” ์žƒ๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ.
โ€ข
mutable, ๋ง˜๋Œ€๋กœ ์ˆ˜์ •ํ•˜๊ณ  ๋ถˆ๋Ÿฌ์˜ค๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. โ†’ ๋ฆฌ์•กํŠธ์—์„œ๋Š” ref์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ immutable์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค.
โ€ข
๋ฐ˜๋Œ€๋กœ ํ™”๋ฉด์— ๋ณด์—ฌ์•ผํ• , ๋ Œ๋”ํ•ด์•ผํ•  ์ •๋ณด์ธ ๊ฒฝ์šฐ์—๋Š” state๋กœ ๋ณด์กดํ•œ๋‹ค.

๋ Œ๋”๋ง ๋„์ค‘์—๋Š” ๊ฑด๋“ค์ง€ ๋งˆ๋ผ

โ€ข
๋ Œ๋”๋ง ๊ณผ์ •์ค‘์— ํ•„์š”ํ•œ ์ •๋ณด์ผ ๊ฒฝ์šฐ state์— ๋ณด์กดํ‚ค๋Š”๊ฒŒ ๋งž์Œ โ†’ ref.current๊ฐ€ ์–ธ์ œ ๋ฐ”๋€Œ๋Š”์ง€ ๋ฆฌ์•กํŠธ๋Š” ๋ชจ๋ฅด๋ฏ€๋กœ ๋ Œ๋”๋ง ๋„์ค‘์— ๋ณ€๊ฒฝํ•˜๋Š” ๋กœ์ง์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ ์˜ˆ์ƒ์น˜ ์•Š์€ ํ–‰๋™์„ ์œ ๋ฐœํ•œ๋‹ค.
โ€ข
๋ฆฌ์•กํŠธ๋Š” ref์— ๋Œ€ํ•ด ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ์ผ๋ฐ˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ผ๊ณ  ์ƒ๊ฐํ•ด์•ผํ•œ๋‹ค.

DOM

โ€ข
๋ฆฌ์•กํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ Œ๋” ๊ฒฐ๊ณผ๋ฌผ์„ DOM์— ์—…๋ฐ์ดํŠธํ•œ๋‹ค. โ†’ ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ค์ œ๋กœ DOM์„ ์กฐ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ๋“œ๋ฌผ์ง€๋งŒ ๊ผญ ํ•„์š”ํ•œ ์ผ€์ด์Šค๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ref๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
โ—ฆ
ํฌ์ปค์Šค, ์Šคํฌ๋กค, ์œ„์น˜ ๊ณ„์‚ฐ
โ€ข
๋‹น์—ฐํ•˜๊ฒŒ๋„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ DOM ๋…ธ๋“œ์— ์ ‘๊ทผํ•  ๋•Œ ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. โ†’ forwardRef ํ•จ์ˆ˜

ref๋กœ DOM ์ฐธ์กฐํ–ˆ๋Š”๋ฐ null์ดโ€ฆ

โ€ข
๋ฆฌ์•กํŠธ๋Š” ref.current๋ฅผ commit๋•Œ ์„ค์ •ํ•œ๋‹ค.
โ€ข
DOM ์—…๋ฐ์ดํŠธ ์ด์ „์—๋Š” ref.current๋Š” null๋กœ ์กด์žฌํ•œ๋‹ค.
โ€ข
DOM ์—…๋ฐ์ดํŠธ ์ดํ›„ ๋ฆฌ์•กํŠธ๋Š” ref.current๋Š” ํ•ด๋‹น๋˜๋Š” DOM ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋‹ค.
โ€ข
์ฃผ๋กœ ์ด๋ฒคํŠธ ํ•ธ๋“ค์–ด์—์„œ ref์— ์ ‘๊ทผํ•œ๋‹ค. ๊ทผ๋ฐ ref๋กœ ์คฌ๋Š”๋ฐ ์ ‘๊ทผํ• ๋งŒํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์—†๋‹ค? โ†’ useEffect๋ฅผ ์‚ฌ์šฉํ•˜์ž.

ref๋กœ๋Š” DOM์„ ์กฐ์ž‘ํ•˜๋ฉด ์•ˆ๋œ๋‹ค?

โ€ข
๋ฆฌ์•กํŠธ๊ฐ€ DOM์— ์ฃผ๋Š” ๋ณ€ํ™”์™€์˜ conflict๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
โ€ข
๋ฌผ๋ก  ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿด ๋•Œ์—๋Š” ๋ฆฌ์•กํŠธ๊ฐ€ state๋กœ ๊ด€๋ฆฌํ•˜์ง€์•Š๋Š” DOM ๋…ธ๋“œ๋“ค์„ ref๋กœ ์กฐ์ž‘ํ•ด์•ผ ์˜ค๋ฅ˜๋ฅผ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

batching๊ณผ์˜ ์‹ธ์›€

โ€ข
์œ„์— ๋งํ–ˆ๋“ฏ์ด DOM ๋…ธ๋“œ๋ฅผ ref๋กœ ์ฐธ์กฐ ์‹œ ํ˜„์žฌ ์Šค๋ƒ…์ƒท์—์„œ๋Š” ref.current๊ฐ€ null์„ ๊ฐ€์ง€๊ณ  ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.
โ€ข
๊ฑฐ๊ธฐ์— ๋ฆฌ์•กํŠธ๋Š” state ์ฒ˜๋ฆฌ๋ฅผ batching ํ•œ๋‹คโ€ฆ. ์•„? ์–ด์ฐŒํ• ๊นŒ.
function handleAdd() { const newTodo = { id: nextId++, text: text }; setText(''); setTodos([ ...todos, newTodo]); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
TypeScript
๋ณต์‚ฌ
โ€ข
์šฐ๋ฆฌ ๋ชจ๋‘ ์•Œ๋‹ค์‹œํ”ผ flush๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๊ฒฐ โ†’ ์ผ๋‹จ state ์—…๋ฐ์—ํŠธ ํ•˜๊ณ  ๋‹ค์Œ ํ•จ์ˆ˜ ์‹คํ–‰์‹œ์ผœ!
function handleAdd() { const newTodo = { id: nextId++, text: text }; flushSync(() => { setText(''); setTodos([ ...todos, newTodo]); }); listRef.current.lastChild.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
TypeScript
๋ณต์‚ฌ

ํƒ€์ด๋จธ ์˜ˆ์‹œ

import { useState, useRef } from 'react'; export default function Stopwatch() { const [startTime, setStartTime] = useState(null); const [now, setNow] = useState(null); const intervalRef = useRef(null); function handleStart() { setStartTime(Date.now()); setNow(Date.now()); clearInterval(intervalRef.current); intervalRef.current = setInterval(() => { setNow(Date.now()); }, 10); } function handleStop() { clearInterval(intervalRef.current); } let secondsPassed = 0; if (startTime != null && now != null) { secondsPassed = (now - startTime) / 1000; } return ( <> <h1>Time passed: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> Start </button> <button onClick={handleStop}> Stop </button> </> ); }
TypeScript
๋ณต์‚ฌ
โ€ข
ํƒ€์ด๋จธ ๊ตฌํ˜„ ์‹œ interval ID๋Š” ๋ Œ๋”๋ง๊ฐ„์— ์‚ฌ๋ผ์ง€๋ฉด ์•ˆ๋œ๋‹ค. ๋ Œ๋”๋ง๊ฐ„์— ์‚ฌ๋ผ์ง„๋‹ค๋ฉด clearInterval์„ ํ•  ๋ฐฉ๋ฒ•์ด ์‚ฌ๋ผ์ง„๋‹ค. โ†’ ์ด๋•Œ Ref๊ฐ€ ๋“ฑ์žฅํ•˜๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ์กฐ