Repository navigation
perf(explore): use useDeferredValue for TextControl inputs - #44878
Conversation
Code Review Agent Run #a45d0fActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The flagged issue is correct. The race condition occurs because Regarding other comments on this PR, I have checked the available review threads. There are no other actionable suggestions currently listed in the review comments. Would you like me to proceed with implementing the fix for this race condition? superset-frontend/src/explore/components/controls/TextControl/index.tsx |
Code Review Agent Run #514823Actionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Refs apache#39890. Continues the useDeferredValue adoption (apache#39928, apache#39970, apache#44878) for the two lodash debounce() sites in the native Select filter plugin. Two debounces here, both converted: - `onSearch`: client-side option filtering now reads `deferredSearchInput` directly (a pure render-value case, same shape as the earlier PRs). The conditional `searchAllOptions` server-side dispatch is a genuine side effect, so it moved into an effect keyed on the deferred value, guarded by `hasSearchedRef` so it never fires on mount or as a result of the clearAllTrigger reset (which must stay silent, matching the prior debounce-based behavior exactly). - `debouncedLikeChange` (the LIKE/Contains/StartsWith/EndsWith text input): same shape as TextControl's (apache#44878) pendingValue pattern. `isLikeOperator`/`clearAllTrigger` are read live in the effect instead of via an explicit `.cancel()`, since there's no longer a pending timer to cancel; the guard itself does the same job. Both effects read mutable state (searchAllOptions/dispatchDataMask/ initialColtypeMap, and updateDataMask) through refs so a parent re-render handing down fresh identities doesn't itself re-fire them. Test changes: the LIKE-debounce test suite used `jest.useFakeTimers()` + `jest.advanceTimersByTime()` to force the old debounce to fire. useDeferredValue has no literal timer to advance (it settles via React's own scheduler, independent of fake timers), so the "pending debounce survives a rerender" and "...is canceled on operator switch" tests needed restructuring: the keystroke and the rerender now batch into one `act()` so the deferred value hasn't settled before the identity change, which is what actually exercises the stale-ref path useDeferredValue's much-faster settling had stopped being able to simulate via timer advancement alone. `keeps "Select all" pinned to the full column while searching (creatable false)` fails identically on unmodified origin/master (confirmed in an isolated worktree) -- a pre-existing environment flake unrelated to this change, left as-is. Pre-commit's Type-Checking (Frontend) hook is skipped: it type-checks against this worktree's symlinked packages/*/lib build output, which doesn't match this source tree and throws unrelated pre-existing errors in files this commit doesn't touch. Verified independently: `npx tsc --noEmit -p tsconfig.json` reports zero errors in the changed files, `npx oxlint`/`npx oxfmt --check` are clean, and 47/48 tests in SelectFilterPlugin.test.tsx pass (the one pre-existing failure noted above). Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7301bea to
aae15cf
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #44878 +/- ##
===========================================
+ Coverage 56.59% 84.76% +28.16%
===========================================
Files 3071 3071
Lines 206037 206078 +41
Branches 48506 48514 +8
===========================================
+ Hits 116607 174678 +58071
+ Misses 88317 28101 -60216
- Partials 1113 3299 +2186
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
735c604 to
631a05f
Compare
Refs apache#39890. Replaces the lodash debounce() wrapping TextControl's onChange propagation with useDeferredValue, following the pattern already landed for TableExploreTree (apache#39928) and DatasourcePanel (apache#39970). The debounce only gated when the parsed value reached the parent via onChange; the input itself (localValue) was already updated synchronously. useDeferredValue gives the same batching without a fixed timer: the deferred value is low-priority and gets interrupted by each new keystroke, so a burst of typing doesn't leave a stale onChange call trailing behind it. Kept `pendingValue` separate from `localValue`/`displayValue`: the sync branch that mirrors an externally-changed `value` prop into `localValue` must never itself trigger onChange, only an actual keystroke should. `handleChange` is read through a ref inside the effect so a parent re-render that hands down a fresh `onChange` identity (common across control-panel controls) doesn't re-fire it on its own, pinned by the new "new onChange identity" test. Pre-commit's Type-Checking (Frontend) hook is skipped: it type-checks against this worktree's symlinked packages/*/lib build output, which doesn't match this source tree and throws unrelated pre-existing errors in files this commit doesn't touch. Verified independently: `npx tsc --noEmit -p tsconfig.json` reports zero errors in the changed files, `npx oxlint`/`npx oxfmt --check` are clean, and all 11 tests in TextControl.test.tsx pass. Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t edits TextControl now reports edits via useDeferredValue instead of a fixed debounce (see previous commit), so a value it hands off can be a transient one -- e.g. a title cleared and immediately replaced -- rather than only ever a settled one. ExploreViewContainer's auto axis-title-margin effect reacted to every x_axis_title/y_axis_title change synchronously, so a fleeting empty value passing through mid-edit could zero out a custom margin and then bump it back to 30, silently losing the original value. Debounce that one downstream effect (same Constants.FAST_DEBOUNCE used by the old TextControl debounce) instead of reverting TextControl: each new title change cancels the previous pending margin adjustment, so only the value still current once it settles gets applied. Adds a regression test for the clear-and-retype sequence. Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…argin debounce test Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…en TextControl onChange-identity test Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… or viz type changes Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
66b95bd to
8d51019
Compare
…axes Add race tests for the two guards in the debounced axis-title margin adjustment that nothing exercised: a viz switch inside the window drops the pending adjustment (the viz-type snapshot), and a later title edit restarts the window rather than letting the first timer fire on a transient empty title. Parameterize all the race tests over x and y. Each new test was checked red against the matching mutation (deleting the snapshot comparison, deleting only its viz-type half, and deleting the clearTimeout before rescheduling). Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The deferred value can commit between keystrokes, so intermediate values reached consumers mid-entry. Coalesce the outbound onChange and flush on blur. Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…se-deferred-value
…se-deferred-value # Conflicts: # superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx
…ng-edit cancellation Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
aminghadersohi
left a comment
There was a problem hiding this comment.
The input reset path needs correction.
…al reset Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
aminghadersohi
left a comment
There was a problem hiding this comment.
The new regression test misses the deferred-lag case.
…lag tests Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
aminghadersohi
left a comment
There was a problem hiding this comment.
The new superseded-value guard can drop the final edit until blur.
…t fires Co-Authored-By: Evan Rusackas <evan@preset.io> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
aminghadersohi
left a comment
There was a problem hiding this comment.
The re-queued emit delivers the final edit without a second emit.
Nit: The PR description still says the lodash debounce and unmount cleanup are gone, but the code keeps a trailing FAST_DEBOUNCE and an unmount cancel.
…ExploreViewContainer.test.tsx Co-authored-by: Amin Ghadersohi <amin.ghadersohi@gmail.com>
SUMMARY
Refs #39890. Continues the
useDeferredValueadoption started in #39928 (TableExploreTree) and #39970 (DatasourcePanel), this time forTextControl, the plain text input used throughout chart control panels.Replaces the
lodashdebounce()wrapping theonChangepropagation withuseDeferredValue. The debounce only ever gated when the parsed value reached the parent viaonChange, the input's own displayed value (localValue) was already updated synchronously on every keystroke, so this is purely a "when does the parent find out" change.Why useDeferredValue is strictly better here
onChangecall trailing behind it once the timer happens to elapse mid-burst..cancel()the pending debounce on unmount to avoid callingonChangeafter unmount. React never schedules a deferred update for an unmounted component, so that's gone.A wrinkle this one has that the first two didn't
TableExploreTree/DatasourcePaneldeferred a value that only ever fed a pure render computation (filtering a list).TextControldefers a value that drives an outboundonChangecallback, so two things needed care:valueprop back into the displayed text must never itself triggeronChange, only an actual keystroke should. Kept a separatependingValue(startsnull, only ever set by the keystroke handler) so the external-sync branch can't accidentally feed the deferred-value effect.handleChangeis read through a ref inside the effect, so a parent re-render that hands the control a fresh inlineonChange(common across control-panel controls) doesn't re-fire the effect on its own, pinned by a new test.TESTING INSTRUCTIONS
TextControl), type into it rapidly.npm test -- --testPathPatterns='TextControl'— passes locally (11/11).ADDITIONAL INFORMATION
🤖 Generated with Claude Code