Repository navigation
Compose: Fix useDebounce losing calls when the debounced function changes - #84192
Mustafabharmal wants to merge 3 commits into
Conversation
🤖 PR meta 🤖🎉 PropsUnlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @ciobanu0151. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. Updated as activity occurs, without notifying anyone named here. Add the 🏷️ LabelsThis pull request needs exactly one label indicating its type, and has 0.
Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you. |
What?
Fixes #35505.
useDebounceno longer cancels scheduled calls when the function passed to it changes. The latest version of the function is called instead.Why?
useDebouncerecreated the debounced function, and cancelled the pending call, wheneverfnchanged. A common pattern (auseCallbackwhose dependencies change after the debounced function is called) meant the call was silently dropped and never ran. Components passing inline functions (e.g.PaletteEditListView) can have a pending call cancelled whenever they re-render within the wait time.Some callers already work around this by wrapping
fninuseEvent(useMarkPersistent,media-fields). This moves that fix into the hook so every caller benefits.How?
fnis wrapped withuseEvent, so the debounced function stays stable across renders but always invokes the latestfn. Scheduled calls are still cancelled whenwait/optionschange or on unmount. The docblock/README no longer asks callers to wrapfninuseCallback.Unit tests are added for
useDebounce(it previously had none).Testing Instructions
trunk: threeNew state from intervallogs appear, then nothing. The debounced callback never runs.New state from debounceis logged ~3 seconds after the last interval tick.npm run test:unit:vitest -- packages/compose/src/hooks/use-debounce.Hard-reload (Cmd/Ctrl+Shift+R) between runs, since
registerPlugincan't register the same name twice.Reproduction snippet
Note: the original snippet in the issue calls the debounced function every 1s with a 3s wait, so it never fires with any debounce implementation (lodash included) while the interval keeps running. The snippet above stops the interval after 3 ticks so the debounce has time to fire.
Screenshots or screencast
Use of AI Tools
I used Claude Code to look into the cause and draft the change and this description. I reviewed and tested the change myself.