Fix Unexpected undefined crash in Combobox component with virtual mode (#3678)
This PR fixes an issue where the `Combobox` component crashes if you are using the `virtual` option and you quickly type something such that the `Combobox` opens but no valid options are available. We already check if the current active index is available in the internal `options` list. However, if you then call `virtualizer.scrollToIndex(data.activeOptionIndex)` it will crash if you are too fast. https://github.com/user-attachments/assets/f48172e6-4098-4a31-aa16-67ce21f074d1 If you are typing slowly, then it will work as expected. https://github.com/user-attachments/assets/9d522bd5-5b54-4c12-9250-a2d92a511b35 I did find an open issue on TanStack's repo about this: https://github.com/TanStack/virtual/issues/879 This PR is basically a workaround by delaying the call. But it does have the expected behavior now. https://github.com/user-attachments/assets/2e5e47a5-b021-4897-b098-568711723b77 Fixes: #3583
This commit is contained in:
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Bump `@tanstack/react-virtual` to fix warnings in React 19 projects ([#3588](https://github.com/tailwindlabs/headlessui/pull/3588))
|
||||
- Fix `aria-invalid` attributes to have a valid `'true'` value ([#3639](https://github.com/tailwindlabs/headlessui/pull/3639))
|
||||
- Add missing `invalid` prop to `Combobox` component ([#3677](https://github.com/tailwindlabs/headlessui/pull/3677))
|
||||
- Fix `Unexpected undefined` crash in `Combobox` component with `virtual` mode ([#3678](https://github.com/tailwindlabs/headlessui/pull/3678))
|
||||
|
||||
## [2.2.0] - 2024-10-25
|
||||
|
||||
|
||||
@@ -479,6 +479,7 @@ function VirtualProvider(props: {
|
||||
children: (data: { option: unknown; open: boolean }) => React.ReactElement
|
||||
}) {
|
||||
let data = useData('VirtualProvider')
|
||||
let d = useDisposables()
|
||||
let { options } = data.virtual!
|
||||
|
||||
let [paddingStart, paddingEnd] = useMemo(() => {
|
||||
@@ -528,6 +529,7 @@ function VirtualProvider(props: {
|
||||
}}
|
||||
ref={(el) => {
|
||||
if (!el) {
|
||||
d.dispose()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -537,9 +539,13 @@ function VirtualProvider(props: {
|
||||
}
|
||||
|
||||
// Scroll to the active index
|
||||
if (data.activeOptionIndex !== null && options.length > data.activeOptionIndex) {
|
||||
virtualizer.scrollToIndex(data.activeOptionIndex)
|
||||
}
|
||||
//
|
||||
// Workaround for: https://github.com/TanStack/virtual/issues/879
|
||||
d.nextFrame(() => {
|
||||
if (data.activeOptionIndex !== null && options.length > data.activeOptionIndex) {
|
||||
virtualizer.scrollToIndex(data.activeOptionIndex)
|
||||
}
|
||||
})
|
||||
}}
|
||||
>
|
||||
{items.map((item) => {
|
||||
|
||||
@@ -108,7 +108,7 @@ function Example({ virtual = true, data, initial }: { virtual?: boolean; data; i
|
||||
<Combobox.Options
|
||||
transition
|
||||
anchor="bottom start"
|
||||
className="w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-1000 [--anchor-gap:theme(spacing.1)] [--anchor-max-height:theme(spacing.60)] focus:outline-none data-[closed]:opacity-0 sm:text-sm sm:leading-5"
|
||||
className="w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:theme(spacing.1)] [--anchor-max-height:theme(spacing.60)] focus:outline-none data-[closed]:opacity-0 sm:text-sm sm:leading-5"
|
||||
>
|
||||
{({ option }) => {
|
||||
return (
|
||||
@@ -157,7 +157,7 @@ function Example({ virtual = true, data, initial }: { virtual?: boolean; data; i
|
||||
<Combobox.Options
|
||||
transition
|
||||
anchor="bottom start"
|
||||
className="w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-1000 [--anchor-gap:theme(spacing.1)] [--anchor-max-height:theme(spacing.60)] focus:outline-none data-[closed]:opacity-0 sm:text-sm sm:leading-5"
|
||||
className="w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:theme(spacing.1)] [--anchor-max-height:theme(spacing.60)] focus:outline-none data-[closed]:opacity-0 sm:text-sm sm:leading-5"
|
||||
>
|
||||
{timezones.map((timezone, idx) => {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user