
Building Snake in React — Canvas RAF Loop, Mutable Refs to Avoid Stale Closures, and Wall Wrap
Snake seems simple — move, eat, grow, repeat. But building it in React has a specific trap: if you use useState for game state inside a requestAnimationFrame loop, every callback captures stale values from the render when it was created. Here's how the Snake game is built — with all mutable game state in refs, timestamp-based speed control, wall wrap, and touch support. The Stale Closure Problem In React, useState values inside a RAF callback are frozen at the time the callback was created. If you write const [snake, setSnake] = useState(...) and read snake inside requestAnimationFrame , you'll always see the initial empty array — even after the snake grows. The fix is to put all game state that the RAF loop needs to read into useRef : const snakeRef = useRef < Pt [] > ([]); const dirRef = useRef < Dir > ( " RIGHT " ); const nextDirRef = useRef < Dir > ( " RIGHT " ); const foodRef = useRef < Pt > ({ x : 5 , y : 5 }); const scoreRef = useRef ( 0 ); const speedRef = useRef ( BASE_SPEED )
Continue reading on Dev.to
Opens in a new tab


