π 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 > fe' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
File API λ₯Ό μ΄μ©ν Input μ»΄ν¬λνΈ λ§λ€κΈ° (2) | 2022.08.13 |
---|---|
Next.js API Routes μμ보기 (2) | 2022.07.26 |
React μ»΄ν¬λνΈ(children) νμ΄ννκΈ° (0) | 2022.07.23 |
React μ μ΄ μ»΄ν¬λνΈ vs λΉμ μ΄ μ»΄ν¬λνΈ (0) | 2022.04.10 |
React μ»΄ν¬λνΈμμ νμ΄λ¨Έ μ€μ νκΈ° (with Hooks) (0) | 2021.03.02 |
π¬ λκΈ