-
Notifications
You must be signed in to change notification settings - Fork 1.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
fix: SSR Combobox inner ref lost #7663
base: main
Are you sure you want to change the base?
Conversation
@snowystinger Took some time as well to look at this. My first intuition was also the fake DOM, but after noticing the issue being fixed by splicing the "non-collection" content from the builder, i was getting suspicious. Especially since only After additional investigation, it appears the issue is stemming from the switch in rendering strategies after initial hydration. The switch causes refs attached inside The issue can only be fixed by maintaining a consistent strategy throughout the render cycle. (e.g. memoizing the In regards to the issuer objective, we currently calculate |
@snowystinger Just pinging here in case it was missed. I can pick this up if you lack resources at the moment 👍 |
Any help is appreciated, i'm hoping to get a chance to look at this one and [RAC] Table components do not play well with Suspense this week with some of the team. |
* feat: auto-adjust menu width for custom triggers * fix: test assert label compatibility * fix: rules of react * feat: popover minWidth for custom trigger elements * fix: use resize observer * chore: reset combobox story
@@ -43,6 +43,8 @@ const hiddenFragment = typeof DocumentFragment !== 'undefined' ? new DocumentFra | |||
export function Hidden(props: {children: ReactNode}) { | |||
let isHidden = useContext(HiddenContext); | |||
let isSSR = useIsSSR(); | |||
// eslint-disable-next-line react-hooks/exhaustive-deps | |||
let wasSSR = useMemo(() => isSSR, []); |
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.
See comment here for why #7710 (comment)
Concern being that we'll now always render into template if it's an SSR app. This may come with a performance hit, we'll need to figure out a way to benchmark this.
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.
Hm, a quick MDN check tells me a template
element without shadow root basically is a DocumentFragment
.
Do you have specific concerns on what may cause reflow or repaint or is it just sanity check for now?
I was also chewing on whether useMemo
may be unsuited here, as React may discard the cache in some scenarios causing our issue to reappear. Maybe a ref
would be the better alternative.
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.
sanity check mostly
even though it's just a document fragment, the nodes would be bigger in memory and there may be more to the logic of parenting/validation/etc, so it may be slower to add and remove nodes even without the consideration of reflow or paint
That's a good call out about the useMemo, I'll update it to a ref
@@ -92,6 +92,27 @@ export const Popover = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pop | |||
let isHidden = useIsHidden(); | |||
let {direction} = useLocale(); | |||
|
|||
// We can set minWidth to the trigger width as a courtesy for custom trigger elements. |
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.
is this something we want to do?
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.
decided it's not, if i have a large trigger but want a smaller popover, then I can't do that now
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 purposefully spread the users style over the minWidth to allow for overrides in case someone needs a smaller popover.
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.
Ah yes, that is correct.
Unfortunately I think it'd also be a breaking change. People may be relying on it being smaller than the trigger width right now. They'd have to override it to get that behaviour back.
It would also add an extra resize observer to every popover which seems excessive.
Maybe it'd be useful to encapsulate the logic from there into a utility hook to help people manage the width. But I don't think we can default so it in every popover.
I verified the fix not just with the test. I also ran verdaccio and added the stackblitz example to it
|
@snowystinger I became curios why React reconciliation was breaking down here so I began digging further. My first suspicion was our Here is what is happening underneath, which I validated by patching
With that knowledge, we can attempt to force <CollectionInner render={props.children} collection={collection} key={useIsSSR() ? 'server' : 'client'} /> It should allow us to revert the fix in Hopefully this is less impactful on the metrics while also preserving a prettier output 👍 |
It looks like it's correct; I've updated the test to reflect the reality a bit better. Thank you so incredibly much for diving into that. |
Closes #7250
See #7663 (comment) for explanation.
I've removed a bit of code from the contribution around mergeProps, it was deemed too much for this PR. We would like to benchmark mergeProps and see if there is a real benefit and how much instead of a theoretical one. That will help us decide if we can risk changing the behaviour.
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project: