From 9f6655e6cd818c1bc85319460928a1f831fc622d Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:47:17 +0100 Subject: [PATCH] fix: unsubscribe from EventClient event listeners on cleanup --- packages/form-core/src/FormApi.ts | 96 ++++++++++++++++++------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 30f909cfe..f3e6feb62 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -49,6 +49,17 @@ import type { import type { DeepKeys, DeepKeysOfType, DeepValue } from './util-types' import type { Updater } from './utils' +const debouncedDevtoolState = throttle( + (state: AnyFormState, id: string) => + formEventClient.emit('form-state', { + id, + state: state, + }), + { + wait: 300, + }, +) + /** * @private */ @@ -1305,47 +1316,6 @@ export class FormApi< this.handleSubmit = this.handleSubmit.bind(this) this.update(opts || {}) - - const debouncedDevtoolState = throttle( - (state: AnyFormState) => - formEventClient.emit('form-state', { - id: this._formId, - state: state, - }), - { - wait: 300, - }, - ) - - // devtool broadcasts - this.store.subscribe(() => { - debouncedDevtoolState(this.store.state) - }) - - // devtool requests - formEventClient.on('request-form-state', (e) => { - if (e.payload.id === this._formId) { - formEventClient.emit('form-api', { - id: this._formId, - state: this.store.state, - options: this.options, - }) - } - }) - - formEventClient.on('request-form-reset', (e) => { - if (e.payload.id === this._formId) { - this.reset() - } - }) - - formEventClient.on('request-form-force-submit', (e) => { - if (e.payload.id === this._formId) { - this._devtoolsSubmissionOverride = true - this.handleSubmit() - this._devtoolsSubmissionOverride = false - } - }) } get formId(): string { @@ -1380,7 +1350,51 @@ export class FormApi< mount = () => { const cleanupFieldMetaDerived = this.fieldMetaDerived.mount() const cleanupStoreDerived = this.store.mount() + + // devtool broadcasts + const cleanupDevtoolBroadcast = this.store.subscribe(() => { + debouncedDevtoolState(this.store.state, this._formId) + }) + + // devtool requests + const cleanupFormStateListener = formEventClient.on( + 'request-form-state', + (e) => { + if (e.payload.id === this._formId) { + formEventClient.emit('form-api', { + id: this._formId, + state: this.store.state, + options: this.options, + }) + } + }, + ) + + const cleanupFormResetListener = formEventClient.on( + 'request-form-reset', + (e) => { + if (e.payload.id === this._formId) { + this.reset() + } + }, + ) + + const cleanupFormForceSubmitListener = formEventClient.on( + 'request-form-force-submit', + (e) => { + if (e.payload.id === this._formId) { + this._devtoolsSubmissionOverride = true + this.handleSubmit() + this._devtoolsSubmissionOverride = false + } + }, + ) + const cleanup = () => { + cleanupFormForceSubmitListener() + cleanupFormResetListener() + cleanupFormStateListener() + cleanupDevtoolBroadcast() cleanupFieldMetaDerived() cleanupStoreDerived()