Files

237 lines
6.4 KiB
Markdown

# Agent Development Guidelines - The Last StarTyper
## React 19 Best Practices
### ⚠️ CRITICAL RULES - NO EXCEPTIONS
1. **NO useCallback** - React 19's compiler handles optimization automatically
2. **NO useMemo** - React 19's compiler handles memoization automatically
3. **NO functions as useEffect dependencies** - Only use primitive values or state values
### Example - What NOT to do:
```typescript
// ❌ BAD - Uses useCallback
const handleClick = useCallback(() => {
doSomething();
}, [dependency]);
// ❌ BAD - Function as dependency
useEffect(() => {
handleClick();
}, [handleClick]); // Never use functions as dependencies!
```
### Example - What to do instead:
```typescript
// ✅ GOOD - Plain function
const handleClick = () => {
doSomething();
};
// ✅ GOOD - Only value dependencies
useEffect(() => {
handleClick();
}, [someStateValue]); // Only primitive/state values
```
## Event Handler Best Practices
### Avoid Stale Closures
When passing objects with methods to hooks, access properties directly inside handlers to avoid stale closures:
```typescript
// ❌ BAD - Destructuring creates stale references
const { gameStatus, startGame } = gameManager;
useEffect(() => {
if (gameStatus === 'ready') startGame();
}, [gameStatus]);
// ✅ GOOD - Direct access ensures fresh values
useEffect(() => {
if (gameManager.gameStatus === 'ready') {
gameManager.startGame();
}
}, [gameManager.gameStatus]);
```
### Keyboard Event Handling
Always prevent default behavior when handling game controls:
```typescript
const handler = (ev: KeyboardEvent) => {
if (ev.code === "Space") {
ev.preventDefault(); // Prevent button activation
// handle space key
}
if (ev.key === "Escape") {
ev.preventDefault(); // Prevent dialog close
// handle escape key
}
};
```
## UI/UX Considerations
### Button Focus Management
Buttons retain focus after clicking, which can interfere with keyboard controls:
```typescript
// Always blur buttons after clicking to prevent Space key from re-triggering
onClick={(e) => {
handleAction();
e.currentTarget.blur(); // Remove focus
}}
```
## Hook Architecture
### State Management
- Use refs for values that need persistence without re-renders
- Use state only for values that trigger UI updates
- Keep game loop data in refs to avoid unnecessary renders
```typescript
// Game loop data - use refs
const lettersRef = useRef<FallingLetter[]>([]);
const playerXRef = useRef<number>(400);
// UI state - use useState
const [gameStatus, setGameStatus] = useState<GameStatus>("waiting");
const [score, setScore] = useState(0);
```
### Effect Dependencies
Only include values that actually trigger the effect logic:
```typescript
// ✅ GOOD - Only necessary dependencies
useEffect(() => {
gameManager.setGameMode(gameMode);
}, [gameMode]); // Only the value that changes
// ❌ BAD - Including objects/functions
useEffect(() => {
gameManager.setGameMode(gameMode);
}, [gameMode, gameManager]); // gameManager is unnecessary
```
## Common Pitfalls & Solutions
### Problem: Game state resets unexpectedly
**Cause**: Multiple state updates or function calls in wrong order
**Solution**: Check for race conditions, ensure proper state checks before updates
### Problem: Keyboard controls not working
**Cause**: DOM element has focus (usually buttons)
**Solution**: Blur active elements, use preventDefault()
### Problem: Stale values in event handlers
**Cause**: Closure over old values when using destructuring
**Solution**: Access object properties directly inside handlers
### Problem: Infinite re-renders
**Cause**: Functions as dependencies or state updates in render
**Solution**: Remove function dependencies, move state updates to effects
## Game-Specific Patterns
### Game Mode Changes
- Only quit active games when changing modes
- Don't reset state unnecessarily
- Preserve user context when possible
```typescript
const changeGameMode = (mode: GameMode) => {
setGameModeState(mode);
// Only quit if actively playing
if (gameStatus === 'started') {
quitGame();
}
};
```
### Animation Frame Loops
- Store animation frame IDs in refs
- Always cleanup on unmount
- Use refs for loop data to avoid re-renders
```typescript
const gameLoopRef = useRef<number | null>(null);
const startLoop = () => {
const loop = (time: number) => {
// Update game state
gameLoopRef.current = requestAnimationFrame(loop);
};
gameLoopRef.current = requestAnimationFrame(loop);
};
const stopLoop = () => {
if (gameLoopRef.current) {
cancelAnimationFrame(gameLoopRef.current);
gameLoopRef.current = null;
}
};
```
## Testing & Debugging
### Browser Focus Issues
When debugging keyboard input issues:
1. Check if any DOM element has focus (inspect element, check document.activeElement)
2. Test with blur() on suspected elements
3. Try unfocusing/refocusing browser window - if this fixes it, it's a focus issue
### State Synchronization
When game state seems out of sync:
1. Check for multiple state sources (refs vs state)
2. Verify effect dependencies are correct
3. Look for race conditions in async operations
4. Add console logs at state transitions
## Code Quality Standards
### Type Safety
- Always use TypeScript types
- Avoid `any` type
- Define explicit return types for hooks
### Naming Conventions
- Hooks: `use[Feature]` (e.g., `useGame`, `useBoard`)
- Event handlers: `handle[Event]` (e.g., `handleTypedLetter`)
- Refs: `[name]Ref` (e.g., `gameLoopRef`, `containerRef`)
- Managers: `[feature]Manager` (e.g., `gameStatusManager`)
### File Organization
```
src/
├── use[Feature]/
│ ├── index.ts # Public exports
│ └── use[Feature].ts # Hook implementation
├── types.ts # All TypeScript types
└── index.ts # Package exports
```
## Performance Considerations
### Minimize Re-renders
- Use refs for non-UI state
- Keep component trees shallow
- Avoid passing new objects/arrays as props
### Canvas Rendering
- Only redraw when necessary
- Use requestAnimationFrame for smooth animations
- Clear only changed regions when possible
## Remember
1. **React 19 = No useCallback/useMemo**
2. **No functions as dependencies**
3. **Blur buttons after clicking**
4. **preventDefault on game controls**
5. **Access object properties directly in handlers**
6. **Use refs for game loop data**
7. **Test focus issues by unfocusing browser**
When in doubt, keep it simple. React 19's compiler will optimize for you.