-
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
|
Closes #7250
Added a SSR test which reproduces the bug. Haven't determined cause yet.
Best guess/working theory is that we let React render into the fake dom for collections. Usually, the last thing to render is the actual dom, so the ref is set correctly in that case. However, in this case, there appears to be an extra render in the fake dom, but not in the real dom, when I have a state change around the ComboBox.
I don't know how to verify this yet, nor how I could fix it if it were happening. At least, not without a custom renderer, which seems a bit heavy.
I attempted to remove the ref from everything in the CollectionBuilder/Document to no avail.
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project: