Adding Custom Methods
Adding Custom Methods
Section titled “Adding Custom Methods”Extend hooks with domain-specific methods for cleaner APIs.
Basic Example
Section titled “Basic Example”export function withPlayer(scene: Phaser.Scene) { const state = withLocalState(scene, 'player', { hp: 100, maxHp: 100 });
return { ...state, // Include all HookState methods
// Add custom methods takeDamage: (amount: number) => { const current = state.get(); state.patch({ hp: Math.max(0, current.hp - amount) }); },
heal: (amount: number) => { const current = state.get(); state.patch({ hp: Math.min(current.maxHp, current.hp + amount) }); } };}
// Usage - clean and expressiveconst player = withPlayer(this);player.takeDamage(30); // Instead of player.patch({ hp: player.get().hp - 30 })player.heal(20);With TypeScript Types
Section titled “With TypeScript Types”import { type HookState, withLocalState } from 'phaser-hooks';
type Player = { hp: number; maxHp: number; level: number;};
type PlayerState = HookState<Player> & { takeDamage: (amount: number) => void; heal: (amount: number) => void; levelUp: () => void; isDead: () => boolean;};
export function withPlayer(scene: Phaser.Scene): PlayerState { const state = withLocalState<Player>(scene, 'player', { hp: 100, maxHp: 100, level: 1 });
return { ...state,
takeDamage: (amount: number) => { const current = state.get(); state.patch({ hp: Math.max(0, current.hp - amount) }); },
heal: (amount: number) => { const current = state.get(); state.patch({ hp: Math.min(current.maxHp, current.hp + amount) }); },
levelUp: () => { const current = state.get(); state.patch({ level: current.level + 1, maxHp: current.maxHp + 10, hp: current.maxHp + 10 }); },
isDead: () => state.get().hp <= 0 };}Patterns
Section titled “Patterns”Validation Methods
Section titled “Validation Methods”canAttack: () => { const p = state.get(); return p.stamina >= 10 && !p.isStunned;}Computed Properties (as methods)
Section titled “Computed Properties (as methods)”healthPercent: () => { const p = state.get(); return (p.hp / p.maxHp) * 100;}Complex Updates
Section titled “Complex Updates”gainExp: (amount: number) => { const current = state.get(); const newExp = current.exp + amount;
if (newExp >= current.expToNextLevel) { // Level up! state.set({ ...current, level: current.level + 1, exp: newExp - current.expToNextLevel, expToNextLevel: calculateNextLevelExp(current.level + 1), maxHp: current.maxHp + 10, hp: current.maxHp + 10 }); } else { state.patch({ exp: newExp }); }}Event Emitters
Section titled “Event Emitters”import { events } from '@/events';
takeDamage: (amount: number) => { const old = state.get(); state.patch({ hp: Math.max(0, old.hp - amount) });
scene.events.emit(events.PLAYER_DAMAGED, { amount, remaining: state.get().hp });
if (state.get().hp === 0) { scene.events.emit(events.PLAYER_DIED); }}When to Add Methods vs When to Keep Simple
Section titled “When to Add Methods vs When to Keep Simple”✅ Add methods when:
- Logic is complex or repeated
- You want expressive API (takeDamage vs patch)
- Validation is needed
- Side effects (events, sounds) are involved
❌ Keep simple when:
- Just reading/writing properties
- One-liner operations
- State is used in many different ways
Next Steps
Section titled “Next Steps”- Composing Multiple Hooks - Combine hooks
- Best Practices - Guidelines