- 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.
74 lines
2.5 KiB
TypeScript
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, []);
|
|
}
|