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 val...

Source: DEV Community
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); const last