Skip to content

Commit af68a34

Browse files
authored
Fix <Popover.Button as={Fragment} /> crash (#1889)
* prevent infinite loop When you use `as={Fragment}` an unmount and remount can happen. This means that the `ref` gets called with `null` for the unmount and `HTMLButtonElement` for the mount. This keeps toggling which results in an infinite loop and eventually a Maximum callback size exceeded issue. This ensures that we only set the button if we have a button. * update changelog
1 parent fdccf07 commit af68a34

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Fix `<Popover.Button as={Fragment} />` crash ([#1889](https://github.com/tailwindlabs/headlessui/pull/1889))
1113

1214
## [1.7.3] - 2022-09-30
1315

packages/@headlessui-react/src/components/popover/popover.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,33 @@ describe('Rendering', () => {
337337
})
338338

339339
describe('Popover.Button', () => {
340+
it(
341+
'should be possible to render a Popover.Button using a fragment',
342+
suppressConsoleLogs(async () => {
343+
render(
344+
<Popover>
345+
<Popover.Button as={Fragment}>
346+
<button>Trigger</button>
347+
</Popover.Button>
348+
<Popover.Panel></Popover.Panel>
349+
</Popover>
350+
)
351+
352+
assertPopoverButton({
353+
state: PopoverState.InvisibleUnmounted,
354+
attributes: { id: 'headlessui-popover-button-1' },
355+
})
356+
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
357+
358+
await click(getPopoverButton())
359+
360+
assertPopoverButton({
361+
state: PopoverState.Visible,
362+
attributes: { id: 'headlessui-popover-button-1' },
363+
})
364+
assertPopoverPanel({ state: PopoverState.Visible })
365+
})
366+
)
340367
it(
341368
'should be possible to render a Popover.Button using a render prop',
342369
suppressConsoleLogs(async () => {

packages/@headlessui-react/src/components/popover/popover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
390390
let buttonRef = useSyncRefs(
391391
internalButtonRef,
392392
ref,
393-
isWithinPanel ? null : (button) => dispatch({ type: ActionTypes.SetButton, button })
393+
isWithinPanel ? null : (button) => button && dispatch({ type: ActionTypes.SetButton, button })
394394
)
395395
let withinPanelButtonRef = useSyncRefs(internalButtonRef, ref)
396396
let ownerDocument = useOwnerDocument(internalButtonRef)

0 commit comments

Comments
 (0)