-
Notifications
You must be signed in to change notification settings - Fork 536
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
Improve the useCombinedRefs
hook by using a Proxy
#2197
Conversation
|
useCombinedRefs
hookuseCombinedRefs
hook by using a Proxy
size-limit report 📦
|
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.
This looks good to me (as someone testing the component that uses this right now on the PRs React team), but I don't have real permissions to approve here.
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.
Looks good to me 👍
It is possible to unit test this hook ensure that it works as we expect? Also, could you add a patch
changeset to this PR?
@colebemis I can definitely add unit tests. How would I add a changeset? |
@iansan5653 You can click this link: That will create a markdown file for you where you can describe your changes. |
Curious about whether we can basically remove this hook entirely (or just wrap the native react implemetation of it) useImperativeHandle(ref, () => internalRef.current)
// used like
forwardRef((props, forwardedRef) => {
const ref = useRef()
useImperativeHandle(forwardedRef, () => ref.current)
return <div ref={ref} />
}) This forwads the 'internal' ref to the potentially forwardedRef already - and is the native react impl of this (which means it's already included in the react bundle) What an imperative handle does While combined refs allows passing many refs, where a hook might be a little less ergonomic since it won't handle being called in a loop well, it gets some of this out of this library we could use the callback to provide multiple refs together, which should limit the extra code we run for this if we need to |
Hmm, that actually might work. I'll bet that 9/10 or more uses of this hook would be resolved by that example - I think it's very rare to actually combine more than one ref. |
Closing this in favor of #2204 - thanks @mattcosta7! |
The
useCombinedRefs
hook is intended to provide a single ref that cascades down to several other refs. This is useful when, for example, you have a forwarded ref (that could be an object or a function) and you want to also have a local ref available as well:When the
combinedRef
is updated, theforwardedRef
is updated as well. There can even be multipleforwardedRef
s that will all get updated.The current implementation uses
useLayoutEffect
to synchronize all the refs on every render. This typically works well, but can have edge cases because refs can update without causing a re-render. This is particularly likely if the ref is being used to hold a value that is not an element or if the ref refers to some element that is a deep child (children could update without re-rendering the parent).This PR changes the implementation to use a
Proxy
as the target ref instead, so that any time the target ref is updated, all dependent refs are updated immediately. No layout effect is required using this strategy.We could have used a function as the target ref instead - this would have made change detection easy without use of a
Proxy
. But there are many use cases where the consumer also wants to get the current value of the ref using.current
and that's not available with a function.This change brings
useCombinedRefs
up to date with the latest code used in production in Projects. I also added the hook tohooks/index
so that we can update that codebase to use the PRC version of the hook and remove some duplication.