EventExtender
A component that extends the event handlers of child components to enable pre/post processing.
Behavior depends on the shouldAwait option:
shouldAwait: false (default)
- Does not await async functions.
- Executes in the order
beforeEvent → original event → afterEvent, but does not wait for each step to complete. - Maintains the normal event call order.
- Example:
mouseDown → mouseUp → clickcall order is preserved.
- Example:
shouldAwait: true
- Awaits async functions.
- Guarantees execution in the order
beforeEvent → original event → afterEvent, waiting for each step to complete. - Note: The normal event call order may change.
- Example: Normally
mouseUpfires beforeclick, but when capturingonMouseUp, themouseUpevent fires afterclick.
- Example: Normally
Code
Interface
typescript
/**
* @description HTML element tag name type (e.g., "div", "span", "input")
*/
type HTMLElementType = keyof JSX.IntrinsicElements;
/**
* @description Events defined in `React.DOMAttributes<HTMLElement>`
* - Only includes event handler names starting with "on" (e.g., "onClick", "onChange", "onSubmit")
*/
type EventNames = keyof React.DOMAttributes<HTMLElement> & `on${string}`;
/**
* @description Generic type that infers the event object type for a specific event on a specific HTML element
* @template K - HTML element type (e.g., "button", "input")
* @template E - Event handler name (e.g., "onClick", "onChange")
* @returns The event object type for that event, or `never`
*/
type ElementEventType<
K extends HTMLElementType,
E extends EventNames
> = JSX.IntrinsicElements[K][E] extends ((e: infer Event) => void) | undefined
? Event
: never;
typescript
interface EventExtenderProps<K extends HTMLElementType, E extends EventNames> {
children: JSX.Element;
capture: E;
shouldAwait?: boolean; // default: false
beforeEvent?: (e: ElementEventType<K, E>) => void | Promise<void>;
afterEvent?: (e: ElementEventType<K, E>) => void | Promise<void>;
}
const EventExtender: <
K extends keyof JSX.IntrinsicElements,
E extends EventNames
>({
children,
capture,
shouldAwait,
beforeEvent,
afterEvent,
}: EventExtenderProps<K, E>) => JSX.Element;
Props
| Name | Type | Default | Description |
|---|---|---|---|
children | JSX.Element | - | The child element whose event handler will be extended |
capture | EventNames | - | The event handler name to capture (e.g., "onClick") |
shouldAwait | boolean | false | Whether to await async event handlers |
beforeEvent | (e: Event) => void | Promise<void> | undefined | Function called before the original event |
afterEvent | (e: Event) => void | Promise<void> | undefined | Function called after the original event |
Usage
Basic Usage
typescript
import { EventExtender } from '@modern-kit/react'
const Example = () => {
return (
<EventExtender
capture="onClick"
beforeEvent={(e: React.MouseEvent<HTMLButtonElement>) => {
console.log('before click', e);
}}
afterEvent={(e: React.MouseEvent<HTMLButtonElement>) => {
console.log('after click', e);
}}>
<button onClick={(e) => console.log('click', e)}>Sync Button</button>
</EventExtender>
);
};
Check the browser developer console.
Async Event Handling
typescript
import { EventExtender } from '@modern-kit/react'
const Example = () => {
return (
<EventExtender
shouldAwait={true} // (*)
capture="onClick"
beforeEvent={async (e: React.MouseEvent<HTMLButtonElement>) => {
await delay(500);
console.log('before click', e);
}}
afterEvent={async (e: React.MouseEvent<HTMLButtonElement>) => {
await delay(500);
console.log('after click', e);
}}>
<button
onClick={async (e: React.MouseEvent<HTMLButtonElement>) => {
await delay(500);
console.log('click', e);
}}>
Async Button
</button>
</EventExtender>
);
};
Check the browser developer console.