Best Practices
Guidelines for writing maintainable custom hooks.
✅ Do’s
Section titled “✅ Do’s”Keep Hooks Focused
Section titled “Keep Hooks Focused”// ✅ Single responsibilityexport function withPlayer(scene) { ... }export function withInventory(scene) { ... }
// ❌ Too much in one hookexport function withEverything(scene) {  // Player, inventory, quests, enemies, UI...}Use TypeScript
Section titled “Use TypeScript”// ✅ Type-safetype PlayerState = HookState<Player> & {  takeDamage: (amount: number) => void;};
export function withPlayer(scene: Phaser.Scene): PlayerState { ... }Provide Defaults
Section titled “Provide Defaults”// ✅ Sensible defaultsexport function withPlayer(scene: Phaser.Scene) {  return withLocalState(scene, 'player', {    hp: 100, // Default    maxHp: 100,    level: 1  });}Use Consistent Naming
Section titled “Use Consistent Naming”// ✅ Consistent conventionwithPlayerwithEnemywithInventorywithSettings❌ Don’ts
Section titled “❌ Don’ts”Don’t Expose Internal State
Section titled “Don’t Expose Internal State”// ❌ Exposes implementationexport function withPlayer(scene) {  const state = withLocalState(...);  return { state, doSomething: () => {} }; // Don't return raw state}
// ✅ Only expose APIexport function withPlayer(scene) {  const state = withLocalState(...);  return { ...state, doSomething: () => {} };}Don’t Create Circular Dependencies
Section titled “Don’t Create Circular Dependencies”// ❌ Circularexport function withPlayer(scene) {  const inventory = withInventory(scene);  // Uses inventory...}
export function withInventory(scene) {  const player = withPlayer(scene); // ❌ Circular!  // Uses player...}
// ✅ Use composition insteadexport function withGame(scene) {  const player = withPlayer(scene);  const inventory = withInventory(scene);  return { player, inventory };}Don’t Put Scene Logic in Hooks
Section titled “Don’t Put Scene Logic in Hooks”// ❌ Hook knows too much about sceneexport function withPlayer(scene) {  const state = withLocalState(...);
  return {    ...state,    die: () => {      scene.scene.start('GameOverScene'); // ❌ Hook shouldn't control scenes    }  };}
// ✅ Let scene handle navigationexport function withPlayer(scene) {  const state = withLocalState(...);
  return {    ...state,    die: () => {      state.set({ hp: 0, isAlive: false });      scene.events.emit('player-died'); // ✅ Emit event instead    }  };}
// In scene:player.on('change', (p) => {  if (!p.isAlive) {    this.scene.start('GameOverScene');  }});Guidelines
Section titled “Guidelines”- One hook = one concern
- Methods should be pure when possible
- Use events for side effects
- Keep hooks testable
- Document complex logic
See Also
Section titled “See Also”- Examples - Real-world hooks