Files
trackgo-fe/packages/core-events/src/hooks.ts
T
shancheas 67ae5b6c11 refactor: improve code formatting and consistency across multiple files
- Standardized import statements and removed unnecessary line breaks for better readability in various components.
- Enhanced error handling and logging in the useElectronPrinter hook.
- Updated sample data formatting in AgGridShowcase for improved clarity.
- Refactored JSX elements for consistent indentation and structure in LandingSample, AuthPage, and EventsPage components.
- Consolidated and simplified conditional rendering logic in several components.

These changes aim to enhance code maintainability and readability throughout the project.
2026-08-25 17:50:48 +07:00

74 lines
2.5 KiB
TypeScript

import { useEffect, useRef, useCallback } from 'react';
import type { AppEvents } from './events.registry';
import { eventBus, publish as busPublish } from './event-bus';
// ─── useAppEvent ────────────────────────────────────────────────
/**
* Subscribe to an application event with automatic cleanup on unmount.
*
* The handler is stored in a ref so that:
* 1. The subscription is stable — re-renders don't cause unsubscribe/resubscribe churn.
* 2. The handler always sees the latest closure values (no stale closures).
* 3. The parent component's render cycle is never triggered by the subscription itself.
*
* @param type - The event name from `AppEvents`.
* @param handler - Callback receiving the typed payload. May be updated on re-render.
*
* @example
* ```tsx
* useAppEvent('AUTH:PROFILE_UPDATED', (profile) => {
* console.log(profile.name); // fully typed, auto-cleaned on unmount
* });
* ```
*/
export function useAppEvent<K extends keyof AppEvents>(type: K, handler: (event: AppEvents[K]) => void): void {
// Always keep the latest handler in a ref to avoid stale closures
// and prevent re-subscription on every render.
const handlerRef = useRef(handler);
handlerRef.current = handler;
useEffect(() => {
// Create a stable delegate that forwards to the latest handler ref
const delegate = (event: AppEvents[K]) => {
handlerRef.current(event);
};
eventBus.on(type, delegate);
// Cleanup: unsubscribe when the component unmounts or `type` changes.
// This is the critical memory-leak prevention mechanism.
return () => {
eventBus.off(type, delegate);
};
}, [type]);
}
// ─── usePublishEvent ────────────────────────────────────────────
/**
* Returns a strictly typed `publish` function.
*
* The returned function is referentially stable (memoized) so it
* can be safely passed as a prop or used in dependency arrays
* without causing unnecessary re-renders.
*
* @example
* ```tsx
* const publish = usePublishEvent();
*
* const handleClick = () => {
* publish('DEVICE:PRINT_RECEIPT', {
* receiptId: '001',
* items: [{ name: 'Widget', qty: 2, price: 9.99 }],
* total: 19.98,
* cashierName: 'Firman',
* timestamp: Date.now(),
* });
* };
* ```
*/
export function usePublishEvent(): typeof busPublish {
return useCallback(busPublish, []);
}