Compose: Support React 19 ref callback cleanups in useMergeRefs#78685
Compose: Support React 19 ref callback cleanups in useMergeRefs#78685tyxla wants to merge 2 commits into
useMergeRefs#78685Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the 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. |
|
Size Change: +56 B (0%) Total Size: 8.18 MB 📦 View Changed
ℹ️ View Unchanged
|
useMergeRefs
| cleanup(); | ||
| } else { | ||
| assignRef( previousRef, null ); | ||
| } |
There was a problem hiding this comment.
This snipped that does cleanup could be extracted into a detachRef function, similar to assignRef. Currently we duplicate the code twice.
| } else { | ||
| currentRefsRef.current.forEach( ( ref, index ) => { | ||
| cleanupsRef.current[ index ] = assignRef( ref, value ); | ||
| } ); |
There was a problem hiding this comment.
What if the result of useMergeRefs was a function that returns a cleanup callback, i.e., the most modern thing? Currently it's still the old function that accepts T | null and returns void.
It wouldn't be usable for React 18 code, but is that a concern?
What
useMergeRefsnow honors the React 19 ref callback cleanup pattern. If an inner ref callback returns a function, that function is invoked at teardown (node change, dependency change, or unmount) instead of the callback being called withnull. Callbacks that don't return a cleanup keep the existingnull-pass behavior, so this is purely additive and none of the 132 call sites need to change.Why
Updating
useMergeRefsto support React 19 ref callback cleanups.This is a prerequisite for deprecating
useRefEffectin favor of nativeuseCallbackref cleanups, because onceuseRefEffectis gone, every migrated call site becomes a plainuseCallbackwhose body returns a cleanup, anduseMergeRefsis the consumer that has to honor it.Part of #71336.
How
assignRefnow returns the function ref's return value (typed() => void | undefined). Object refs continue to set.currentand returnundefined.cleanupsRefstores the cleanup returned by each inner ref.nullpath;useLayoutEffectdep-change path) prefer the stored cleanup over calling the ref withnull.nullbyuseMergeRefs. This matches React 19's own behavior for native refs.useRefEffectto the React 19-nativeuseCallbackpattern.Out of scope:
useRefEffectsource, tests, and call sites, untouched. That's the next checkbox.useMergeRefscall sites, none of their behavior changes.Testing instructions
npm run test:unit -- packages/compose/src/hooks/use-merge-refs, 14 tests pass (7 existing, regression-protected; 7 new for the cleanup path).