
π requestAnimationFrame + React Hook
νΈλ¦¬ν μ¬μ κ³Όμ μμ μ λλ©μ΄μ
μ ꡬνν λ rAF(requestAnimationFrame)μ μ¬μ©νμ΅λλ€.
CSS λ§μΌλ‘λ ꡬνμ΄ νλ€λ€κ³ μκ°λμ΄ μ¬μ©ν APIμΈλ° Reactμ ν¨κ» μ¬μ©νλ©°
λ§μ£Όνλ μ΄μλ₯Ό μ 리νλ €κ³ ν¬μ€ν μ λ¨κΈ°κ² λμμ΅λλ€.
π rAFλ₯Ό μ¬μ©ν slotMachine ꡬνλΆ

μ μΉμ μμ μ¬μ©μ μ§νλ₯Ό λνλ΄λ μ«μ λΆλΆμ μ¦κ° μλκ°
μ²μ²ν κ°μνλ μ λλ©μ΄μ
μ ꡬννκΈ° μν΄ rAFλ₯Ό μ¬μ©νμ΅λλ€.
ꡬν μ½λλ λλ΅ λ€μκ³Ό κ°μ΅λλ€.
ν΄λΉ μ»΄ν¬λνΈκ° μ΅μ΄μ λ λλ§ λ λ μ λλ©μ΄μ
μ μμνκΈ° μν΄ useEffectλ₯Ό μ¬μ©νμκ³
λͺ©ν μ«μμ λλ¬ν λκΉμ§ μ¬κ·μ μΌλ‘ μ λλ©μ΄μ ν¨μλ₯Ό μ€νν©λλ€.
const SlotMachine = ({ startNumber, targetNumber, duration }) => {
const [count, setCount] = useState(0)
const animationId = useRef(0)
useEffect(() => {
const increaseNumberAnimation = (timestamp: number) => {
/** β
λͺ©ν μ«μμ λλ¬ν λκΉμ§ μ λλ©μ΄μ
μ λ°λ³΅ν©λλ€. */
if (count < targetNumber) {
/** β
λ€μ νλ μμ 보μ¬μ€μΌνλ μ«μλ₯Ό κ³μ°ν λ€ */
const next = getNextFrameNumber(timestamp)
/** β
μ΄ μ«μλ₯Ό μνκ°μ μ
λ°μ΄νΈν΄μ νλ©΄μ λ€μ λ λλ§ν©λλ€. */
setCount(next)
animationId.current = requestAnimationFrame(increaseNumberAnimation)
}
}
animationId.current = requestAnimationFrame(increaseNumberAnimation)
return () => cancelAnimationFrame(animationId.current)
})
return <data>{ count }</data>
}
νμ§λ§ μμ κ°μ΄ ꡬννλ©΄ increaseNumberAnimation ν¨μκ° λ¬΄νμ νΈμΆλκ² λ©λλ€.
μ μ΄λ° λ¬Έμ κ° λ°μνλ κ²μΌκΉμ?
π μ€λλ ν΄λ‘μ (Stale Closure) λ?
μ€λλ ν΄λ‘μ (stale closure)λ React Hookμ μ¬μ©ν λ λΉλ²ν λ°μλλ μ΄μμ
λλ€.
μ΄ νμμ΄ λ°μνλ©΄ μν κ°μ λ³νκ° λ°μν΄λ μ΄λ₯Ό κ°μ§νμ§ λͺ»νκ³ μμ κ°μ λ°λΌλ³΄κ² λ©λλ€.
κ·Έλ λ€λ©΄ JavaScript μμ κΌ λ±μ₯νλ κ°λ μΈ ν΄λ‘μ λ 무μμΌκΉμ?
ν΄λ‘μ λ μλͺ μ£ΌκΈ°κ° λλ μΈλΆν¨μμ λ³μλ₯Ό μ°Έμ‘°νλ ν¨μλ₯Ό μλ―Έν©λλ€.
μ΄μ μμμ λ°μν λ¬Έμ μ μμΈμ μ΄ν΄λ³΄κ² μ΅λλ€.
Reactμ useStateλ μν κ°μ count λΌλ λ³μμ λ겨μ€λλ€.
κ·Έλ¦¬κ³ increaseNumberAnimationλΌλ ν¨μκ° μ΄κΈ°νλ λ count κ°μ μ°Έμ‘°νλλ°,
μ΄λ λ³νλ count κ°μΌλ‘ increaseNumberAnimation ν¨μλ₯Ό μ¬ν λΉνμ§ μμΌλ©΄
μ΄κΈ°μ count κ°μΈ 0 μ κ³μ λ°λΌλ³΄κ³ μκ² λ©λλ€.
π Stale Closure λ¬Έμ ν΄κ²°νκΈ°
κ·Έλ λ€λ©΄ μ΄ λ¬Έμ λ₯Ό μ΄λ»κ² ν΄κ²°ν μ μμκΉμ?
μ κ° μκ°ν λ°©λ²μ λ κ°μ§μ΄λ©° μν©μ λ°λΌ μ μ ν λ°©λ²μ μ ννλ©΄ λ©λλ€.
1οΈβ£ useEffectμ μμ‘΄μ± μλ €μ£ΌκΈ°
useEffectμ μμ‘΄μ± λ°°μ΄λ‘ countλ₯Ό μ λ¬νλ©΄ countμ λ³νλ₯Ό κ°μ§ν΄μ
μλ‘μ΄ increaseNumberAnimation ν¨μλ₯Ό μμ±ν μ μμ΅λλ€.
useEffect(() => {
const increaseNumberAnimation = (timestamp: number) => {
// ...
}
// ...
}, [count]) // <-- count μΆκ°!
νμ§λ§ μ΄λ κ² νλ©΄ count κ° μ¦κ°ν λλ§λ€ requestAnimationFrame ν¨μμ
cancelAnimationFrame ν¨μκ° λͺ©ν μ«μμ λλ¬ν λκΉμ§ λ°λ³΅μ μΌλ‘ μ€νλκΈ° λλ¬Έμ
μ±λ₯μμΌλ‘ λ¬Έμ κ° λ μλ μμ κ²μ΄λΌκ³ μκ°νμ΅λλ€.
κ·Έλμ μ΄λ² κ²½μ°μλ μ΄ λ°©λ²λ³΄λ€λ useRef λ₯Ό νμ©ν λ λ²μ§Έ λ°©λ²μ μ¬μ©νμ΅λλ€.
2οΈβ£ useRef λ₯Ό μ΄μ©ν΄μ μν κ° κ΄λ¦¬νκΈ°
React 곡μ κ°μ΄λ λ¬Έμμ 보면 useRef λ₯Ό μ΄μ©ν΄μ μ»΄ν¬λνΈμ μ§μλ³μμ κ°μ μν μ
μνν μ μλ€κ³ μλ΄νκ³ μμ΅λλ€.
useRef μ λ°ν κ°μ λ³μμ μ°Έμ‘°κ°μ΄κΈ° λλ¬Έμ μ°Έμ‘°κ°μ΄ κ°λ¦¬ν€λ λ³μ«κ°μ μ
λ°μ΄νΈνλ©΄
μ°Έμ‘°κ°μ λ³νμ§ μλλΌλ μ΅μ κ°μ μμ νκ² κ°μ Έμ¬ μ μμ΅λλ€.
μ΄λ₯Ό μν΄ νμ¬ count κ°μ μ μ₯νλ tick μ μ μΈν©λλ€.
const SlotMachine = ({ startNumber, targetNumber, duration }) => {
const [count, setCount] = useState(0)
const animationId = useRef(0)
const tick = useRef(0)
useEffect(() => {
const increaseNumberAnimation = (timestamp: number) => {
/** β
tick.current λ‘ μ‘°κ±΄ κ²μ¬ */
if (tick.current < targetNumber) {
const next = getNextFrameNumber(timestamp)
/** β
μ΄ μ«μλ₯Ό μνκ°μ μ
λ°μ΄νΈν΄μ νλ©΄μ λ€μ λ λλ§ν©λλ€. */
setCount(next)
tick.current = next // <-- μΆκ°!
animationId.current = requestAnimationFrame(increaseNumberAnimation)
}
}
// ...
})
return <data>{ count }</data>
}
π μ°Έκ³ μλ£
Be Aware of Stale Closures when Using React Hooks
[λ²μ] μ¬μΈ΅ λΆμ: React Hookμ μ€μ λ‘ μ΄λ»κ² λμν κΉ?
'π¨βπ» web.dev > frontend' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
| [JavaScript] Debounce μ Throttle μ λν΄ μμ보μ (0) | 2022.07.09 |
|---|---|
| [TypeScript] enum κ³Ό union type, μΈμ μ¨μΌν κΉ? (2) | 2022.07.09 |
| React μ μ΄ μ»΄ν¬λνΈ vs λΉμ μ΄ μ»΄ν¬λνΈ (0) | 2022.04.10 |
| [JavaScript] Array.sort λ μμ μ±μ 보μ₯ν κΉ? (0) | 2022.03.09 |
| [TypeScript] js.map νμΌμ 무μμΌκΉ? (0) | 2022.03.07 |
π¬ λκΈ