State Management Best Practices

React state management in the RMMZ/RPGReact context has unique challenges due to the integration with game engine observables, animation loops, and event-driven architecture.

Core Principles

1. Choose the Right State Type

Use useState for:

  • UI-only state (modal open/closed, selected tab, form inputs)

  • State that triggers re-renders when changed

  • Values that change infrequently

const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedTab, setSelectedTab] = useState('items');

Use useRef for:

  • Values that change frequently but don't need to trigger re-renders

  • Animation frame IDs

  • Timers and intervals

  • Tracking previous values

  • Avoiding stale closures in callbacks

const frameRef = useRef<number>(0);
const previousValueRef = useRef<number>(0);

Use MobX observables for:

  • Game data that comes from RMMZ (already observable)

  • Computed values derived from game state

  • Cross-component reactive state

2. Avoid Stale Closures

Problem: Closures in animation loops, event handlers, and setTimeout capture state at creation time.

Solution 1: Use refs to track latest values

Solution 2: Depend on the animation state, not external state

3. Memoize Expensive Computations

Problem: Recreating arrays/objects on every render causes child components to re-render unnecessarily.

Solution: Use useMemo

When to use useMemo:

  • Filtering/mapping large arrays

  • Creating configuration objects passed to children

  • Expensive calculations

  • When passing arrays/objects as props to components using React.memo()

When NOT to use useMemo:

  • Simple calculations (useMemo overhead > calculation cost)

  • Values only used once in the component

  • Primitives (numbers, strings, booleans)

4. Clean Up Side Effects

Problem: Animation frames, timers, and event listeners can leak if not cleaned up.

Solution: Return cleanup function

5. Batch State Updates

Problem: Multiple setState calls in quick succession cause multiple re-renders.

Solution: Combine into single state object

Or use React's automatic batching (React 18+):

6. Functional Updates for Dependent State

Problem: setState using current state can be stale in rapid updates.

Solution: Use functional update form

7. Separate Concerns: Refs for Animation, State for UI

Pattern for animations:

Common Patterns

Pattern: Animation Controller with Skip

Pattern: Tracking Selection in Lists

Pattern: Conditional State Updates

Debugging Tips

1. Track why components re-render

2. Detect stale closures

3. Verify ref updates

Performance Optimization

Use React.memo for expensive components

Avoid inline function creation in render

Use keys properly in lists

See Also

Last updated