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