shadcn-vue
shadcn-vue copied to clipboard
[Bug]: Combobox - Maximum recursive updates exceeded
Reproduction
https://stackblitz.com/edit/is2sf6?file=src%2FApp.vue
Describe the bug
Combobox is unable to render more than 100 elements. This can be easily reproduced when altering any combobox example from the docs to show more than 100 elements at a time.
Opening the popover results in the <CommandEmpty> fallback to be displayed.
When opening the popover, this error message gets thrown in the browser console:
Maximum recursive updates exceeded. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.
I have read a bit about it and this radix-vue issue seems related: https://github.com/radix-vue/radix-vue/issues/551
System Info
System:
OS: macOS 14.2
CPU: (10) arm64 Apple M1 Pro
Memory: 73.25 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.17.0 - ~/.nvm/versions/node/v18.17.0/bin/node
npm: 9.6.7 - ~/.nvm/versions/node/v18.17.0/bin/npm
Browsers:
Chrome: 122.0.6261.94
Firefox: 119.0
Safari: 17.2
npmPackages:
@vueuse/core: ^10.9.0 => 10.9.0
radix-vue: ^1.4.9 => 1.4.9
vue: ^3.4.19 => 3.4.19
Contributes
- [ ] I am willing to submit a PR to fix this issue
- [ ] I am willing to submit a PR with failing tests
I am also having the same issue. Is there a workaround?
I have this issue as well. I figured that until 100 items go fine, everything above 100 items will cause this issue 🤔
Check the example: https://stackblitz.com/~/github.com/devbyray/shadcn-vue-combobox-issue
I'm also having this issue, I have ~100/200 items
I'm also having this issue, 300 items
same problem here. works for under ~100 items
It looks like it is fixed 💪🥳
With the latest version of radix, it no longer shows this error but it's still VERY slow for larger lists. I think ListboxVirtualizer may fix the issue but I don't know if it's directly usable here.
the fix works for me!
maybe you can try this as workaround
With the latest version of radix-vue (1.8.5) the issue is back: https://stackblitz.com/edit/is2sf6-qsxvsu?file=src%2FApp.vue
The error message is slightly different:
Uncaught (in promise) Maximum recursive updates exceeded in component <ComboboxInput>. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.
When you comment out the <CommandInput>
the combobox works fine.
Any ideas?
I have the same issue in v1.9.0
a weird fix, for those using nuxt 3, go to CommandInput.vue
and wrap the whole thing btween a ClientOnly
component, like this:
<ClientOnly>
<div class="flex items-center border-b px-3" cmdk-input-wrapper>
<Search class="mr-2 size-4 shrink-0 opacity-50" />
<ComboboxInput
v-bind="{ ...forwardedProps, ...$attrs }"
auto-focus
:class="cn('flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
/>
</div>
</ClientOnly>
somehow this fixes it and it works with larger arrays of items
a weird fix, for those using nuxt 3, go to
CommandInput.vue
and wrap the whole thing btween aClientOnly
component, like this:<ClientOnly> <div class="flex items-center border-b px-3" cmdk-input-wrapper> <Search class="mr-2 size-4 shrink-0 opacity-50" /> <ComboboxInput v-bind="{ ...forwardedProps, ...$attrs }" auto-focus :class="cn('flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)" /> </div> </ClientOnly>
somehow this fixes it and it works with larger arrays of items
Do you have a fix for Vite?
@phuclh maybe try to add const isBrowser = typeof window !== 'undefined';
and add it to the parent div as v-if="isBrowser"
didnt test it, just speclating that this what clientOnly
does in nuxt 3
I have 1000 entries, getting same error, so I implemented solution provided by @lazbeta for now. Probably would be optimized to implement async search for larger datasets, but I am not getting any lags while rendering the list (tested on local machine).
NOTE: using Inertia.js, not Nuxt
In script setup:
const usersComboboxOpen = ref(false);
const usersSearchTerm = ref("");
const users = usePage().props.users;
const userEntriesLimit = 50;
const filteredUsers = computed(() => {
if (usersSearchTerm.value.length < 3) {
return users.slice(0, userEntriesLimit);
}
const filtered = users.filter((user) =>
user.label.toLowerCase().includes(usersSearchTerm.value.toLowerCase())
);
if (filtered.length > userEntriesLimit) {
return filtered.slice(0, userEntriesLimit);
}
return filtered;
});
In template:
<Popover v-model:open="usersComboboxOpen">
<PopoverTrigger as-child>
<Button
variant="outline"
role="combobox"
:aria-expanded="usersComboboxOpen"
class="justify-between px-3"
>
{{
form.user_id
? users.find((user) => user.value === form.user_id)?.label
: "Select user..."
}}
<ChevronsUpDownIcon class="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0">
<Command v-model:searchTerm="usersSearchTerm">
<CommandInput
class="h-9"
placeholder="Search user..."
/>
<CommandEmpty>No users found.</CommandEmpty>
<CommandList
<CommandGroup>
<CommandItem
v-for="user in filteredUsers"
:key="user.value"
:value="user.label"
@select="
() => {
form.user_id = user.value;
usersComboboxOpen = false;
}
"
>
{{ user.label }}
<CheckIcon
:class="
cn(
'ml-auto size-4 shrink-0',
form.user_id === user.value
? 'opacity-100'
: 'opacity-0'
)
"
/>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
Seems it's working good in v1.9.3, and it should be rendered better in v2
Closing as a stale issue
I confirm it works with latest (1.9.5) version. Problem was in 1.9.1 for me. I tried with 1000 and 250 items and both work. The 1000 one has some minor "lag" so I still prefer the solution above which limits the entire list. 250 items is instant render.