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