-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add useMergeRefs hook #27768
Add useMergeRefs hook #27768
Conversation
9c24593
to
457db3a
Compare
Size Change: -415 B (0%) Total Size: 1.36 MB
ℹ️ View Unchanged
|
// - The value changes. | ||
// - The ref callback has changed (e.g. an updated dependency). | ||
if ( | ||
value !== lastValue.current || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe comparing to the previous value is not useful because if one of the ref changes, React will first call the old function with "null" and then call the new function with the new node which mean this check is always true which also means that the implementation here is equivalent to useCallback( merge(refs), refs )
and in both situations we can't have a callback with dependencies as ref because if it changes, all the other merged refs, even the ones that didn't change,w ill be called again (creating memory leaks or unexpected behavior)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all the other merged refs, even the ones that didn't change,w ill be called again
No, they won't be called since we check if the callback has been changed. Only if it's changed we call it, and we leave any others that remain the same alone. The value check is there to ensure that, even if the callback didn't change, the ref callback is still called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added clearer documentation in ece1982. I tested ref callbacks with and without dependencies and they both work as expected. If a dependency changes, old callback is called with null
, the new with the node. If no dependency changes, the callback is never called, even if other ref callback dependencies change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to add some test cases tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the failing test case:
const ref1 = useCallback( dosomething, [dep1]);
const ref2 = useCallback( dosomething2, [] );
const ref = useMergeRef( [ ref1, ref2 ] );
if the node doesn't change and dep1 change, dosomething2
will be called again while it shouldn't be. (I didn't test but that's my read of the code) Let's add this unit test and see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, it's calling the wrong callback. Changed the code and started adding tests, but it's still a work in progress.
@youknowriad I think I figured it out now. The new approach seems to work well in tests. |
Noting that I had issues when upgrading I like the idea of replacing this 10 lines long utility with a hook that has additional optimizations 👍🏻 |
8e82d20
to
b3d1a12
Compare
Merging since we need this for several PRs that want to add dependencies to ref callbacks, which will currently cause the callbacks to be called too many times. |
* Try: useMergeRefs * update lock file * Rebase fixes * Add docs * Clearer documentation * wip * wip * Add docs for unit tests * Convert new additions
Description
See #27675 (comment).
This
useMergeRefs
hook would allow you to merge refs even if any if any ref callbacks have dependencies. The trick is to have the wrapping ref called as many times as all the dependencies combined, but to check for each ref individually if it wants to be called, that is, when the value of the ref changed or when the ref callback itself changed.The problem with the external
mergeRefs
function is that is calls all merged callbacks on every render and doesn't respect refs wrapped inuseCallback
.How has this been tested?
Screenshots
Types of changes
Checklist: