Created
July 28, 2025 09:22
-
-
Save mimshins/c8bb6afe311e9909266f743c46b7c257 to your computer and use it in GitHub Desktop.
Flush custom event dispatch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { flushSync } from "react-dom"; | |
/** | |
* Flush custom event dispatch. | |
* | |
* React batches *all* event handlers since version 18, this introduces certain considerations when using custom event types. | |
* | |
* Internally, React prioritises events in the following order: | |
* - discrete | |
* - continuous | |
* - default | |
* | |
* https://github.com/facebook/react/blob/a8a4742f1c54493df00da648a3f9d26e3db9c8b5/packages/react-dom/src/events/ReactDOMEventListener.js#L294-L350 | |
* | |
* `discrete` is an important distinction as updates within these events are applied immediately. | |
* React however, is not able to infer the priority of custom event types due to how they are detected internally. | |
* Because of this, it's possible for updates from custom events to be unexpectedly batched when | |
* dispatched by another `discrete` event. | |
* | |
* In order to ensure that updates from custom events are applied predictably, we need to manually flush the batch. | |
* This utility should be used when dispatching a custom event from within another `discrete` event, this utility | |
* is not nessesary when dispatching known event types, or if dispatching a custom type inside a non-discrete event. | |
* For example: | |
* | |
* dispatching a known click 👎\ | |
* `target.dispatchEvent(new Event(‘click’))` | |
* | |
* dispatching a custom type within a non-discrete event 👎\ | |
* `onScroll={(event) => event.target.dispatchEvent(new CustomEvent(‘customType’))}` | |
* | |
* dispatching a custom type within a `discrete` event 👍\ | |
* `onPointerDown={(event) => dispatchDiscreteCustomEvent(event.target, new CustomEvent(‘customType’))}` | |
* | |
* Note: though React classifies `focus`, `focusin` and `focusout` events as `discrete`, it's not recommended to use | |
* this utility with them. This is because it's possible for those handlers to be called implicitly during render | |
* e.g. when focus is within a component as it is unmounted, or when managing focus on mount. | |
* | |
* @param target The target to dispatch its event. | |
* @param event The event to be dispatched. | |
*/ | |
const flushDispatchCustomEvent = <E extends CustomEvent>( | |
target: E["target"], | |
event: E, | |
) => { | |
if (!target) return; | |
flushSync(() => { | |
target.dispatchEvent(event); | |
}); | |
}; | |
export default flushDispatchCustomEvent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment