Repository navigation
Conversation
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
n-input-numbersupports press-and-hold on the add / minus buttons: after 800 ms thevalue starts repeating every 100 ms. Nothing stops that repeat when the component is
destroyed, because the only way out is a
mouseuplistener ondocumentand thecomponent has no unmount hook. So if the input number is unmounted while its button is
being held — it sits in a
v-ifblock, in a row of a dynamic list that gets deleted, in amodal / drawer / popover that is closed (
destroy-on-close, Esc) — then:update:valuekeeps firing every 100 ms into the still-mounted parent, so the boundmodel keeps being rewritten after the control has left the page;
mouseupfinally reachesdocument— ifthe pointer is released outside the browser window, or the user alt-tabs away while
holding the button, it never stops and the destroyed instance is retained by the pending
timer and by the
documentlistener.Reproduction as a user action:
<n-input-number v-if="show" v-model:value="count" />next to a<n-button @click="show = false">remove</n-button>.input number is removed from the tree (the row is deleted, the modal closes, the tab is
switched away — anything that unmounts it before the
mouseuparrives).countkeeps increasing bystepevery 100 ms, indefinitely, although the control isgone from the page.
Root cause
handleAddMousedown/handleMinusMousedownschedule awindow.setTimeoutthat starts awindow.setInterval, and they rely solely on adocumentmouseuplistener to cancelthem:
src/input-number/src/InputNumber.tsx:512-519—firstMinusMousedownId = window.setTimeout(...),minusHoldStateIntervalId = window.setInterval(doMinus, HOLDING_CHANGE_INTERVAL),on('mouseup', document, clearMinusHoldTimeout, { once: true })src/input-number/src/InputNumber.tsx:523-530— the same pair for the add buttonThe component has no
onBeforeUnmount/onUnmountedat all, so unmounting leaves bothtimers running and the
documentlistener attached. Every tick then callsdoAdd/doMinus, which reachemit('update:value', ...).Fix
Release the hold timers and the
documentlisteners when the component unmounts(
src/input-number/src/InputNumber.tsx):offandonBeforeUnmountare added to the existingevtd/vueimport statements.Test
src/input-number/tests/InputNumber.spec.tsx— for each of the two buttons: hold thebutton with fake timers, assert the repeat actually started, unmount, advance the timers
again and assert no further
update:valueis emitted.Red (before the fix):
Green (after the fix), whole
src/input-numbersuite:Commands run:
Note:
eslintcould not be run in this clone —node_modules/.binis missing andnode_modules/eslintresolves to an empty directory, so the toolchain is not installed.The change is limited to one existing import line, one existing import list, and one new
hook placed next to the timers it cancels, so the repo's lint-staged step in CI should not
flag anything.
Prepared with an AI coding agent.