In the previous lesson, you wired up player actions and reducer updates. As a quick reminder, your GameActions component dispatches events, and the reducer applies guarded, immutable updates. Today, you will make the café feel alive by introducing time. You will create a game loop that dispatches a TICK every second, pause it based on state, and clean up intervals to avoid leaks.
We attach the loop in the screen component with a useEffect that sets up a setInterval. The interval dispatches TICK once per second only when the game is running and not paused. Cleanup ensures we stop the timer when the component unmounts or the dependencies change.
src/components/GameScreen.jsx
What is happening:
- Guard clause: If we are not in the
PLAYINGphase or the game is paused, do not start an interval. - Interval: Dispatches
{ type: 'TICK' }every 1000 ms. - Cleanup:
clearIntervalprevents memory leaks and duplicate timers. - Dependency array: Restarting the interval when
phaseorisPausedchanges ensures the loop tracks the current state.
The reducer is still the “brain.” For now, each tick reduces the remaining time and converts real seconds to in-game hours and minutes (1 sec = 3 game minutes). You will expand this logic in the next unit.
src/reducers/gameReducer.js
Key ideas:
- Guard early if not
PLAYINGor paused to keep updates predictable. - Convert remaining real-time seconds into a fast-forwarded in-game clock.
- Return a new state object to maintain immutability and clean re-renders.
You now have a reliable game loop:
- A
useEffect-backed interval that ticks only when the game is active. - Proper cleanup to avoid multiple overlapping timers.
- A reducer that updates time consistently and safely.
Next, you will practice making the loop feel real with events and dynamic state. Let’s head to the practice section and bring more motion into the café.
