[Bug] The List scrolls to a stale top item when search changes
When you change the search search string and this change makes more search results then the list scrolls to the previous top item instead of the current one.
I suppose it happens because store.setState('search', props.value) is followed by scrollSelectedIntoView... and inside getSelectedItem search for [aria-selected="true"] in not yet updated dom.
When the search string is changed from cc to cccc - the new top item is "Cascade Interactive" When the search is changed back from cccc to cc - the new top item is "Echo Networks" and it is selected BUT List scrolls to Cascade Interactive
https://github.com/user-attachments/assets/f7837b05-dbc1-44ab-b9ea-49cf5549a6b5
here is a test data
const organizations = [ { id: 1, name: "Acme Innovations" }, { id: 2, name: "BlueSky Technologies" }, { id: 3, name: "Quantum Dynamics" }, { id: 4, name: "Aurora Systems" }, { id: 5, name: "Nimbus Labs" }, { id: 6, name: "Vertex Analytics" }, { id: 7, name: "Solaris Consulting" }, { id: 8, name: "Evergreen Solutions" }, { id: 9, name: "Summit Digital" }, { id: 10, name: "Atlas Ventures" }, { id: 11, name: "NeonSoft" }, { id: 12, name: "CorePulse" }, { id: 13, name: "Zenith Global" }, { id: 14, name: "Ironclad Industries" }, { id: 15, name: "Frontier Robotics" }, { id: 16, name: "Lighthouse Data" }, { id: 17, name: "Horizon Partners" }, { id: 18, name: "NextGen BioWorks" }, { id: 19, name: "Cascade Interactive" }, { id: 20, name: "Echo Networks" }, { id: 21, name: "Velocity Ventures" }, { id: 22, name: "PolarEdge" }, { id: 23, name: "SummitOne Group" }, { id: 24, name: "Cobalt Consulting" }, { id: 25, name: "Helix Cloud" }, { id: 26, name: "NovaLink Systems" }, { id: 27, name: "TrueNorth Media" }, { id: 28, name: "BrightPath AI" }, { id: 29, name: "EchoBase Security" }, { id: 30, name: "TerraWorks" }, ];
The workaround for this is to manually scroll to top on value change
<CommandInput
placeholder="Search organization..."
onValueChange={() => {
setTimeout(() => {
commandListRef.current?.scrollTo({ top: 0, behavior: 'smooth' });
});
}}
/>
<CommandList ref={commandListRef} >
....
Facing this same issue, can confirm the workaround above works! Hoping for a native fix in the future 🤞. For anyone using shadcn/ui here's how I implemented this workaround in command.tsx
const CommandContext = React.createContext<{
listRef: React.RefObject<HTMLDivElement | null>;
}>({ listRef: { current: null } });
// ...
function Command({ className, ...props }: CommandProps) {
const listRef = React.useRef<HTMLDivElement | null>(null);
return (
<CommandContext.Provider value={{ listRef }}>
<CommandPrimitive
data-slot="command"
className={cn(
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
className
)}
{...props}
/>
</CommandContext.Provider>
);
}
// ...
function CommandInput({
className,
onValueChange,
...props
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
const { listRef } = React.useContext(CommandContext);
return (
<div
data-slot="command-input-wrapper"
className="flex h-9 items-center gap-2 border-b px-2"
>
<SearchIcon className="size-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
data-slot="command-input"
className={cn(
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
className
)}
onValueChange={(value) => {
onValueChange?.(value);
setTimeout(() => {
listRef.current?.scrollTo({ top: 0, behavior: 'instant' });
});
}}
{...props}
/>
</div>
);
}
// ...
function CommandList({
className,
...props
}: Omit<React.ComponentProps<typeof CommandPrimitive.List>, 'ref'>) {
const { listRef } = React.useContext(CommandContext);
return (
<CommandPrimitive.List
data-slot="command-list"
className={cn(
'max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto',
className
)}
ref={listRef}
{...props}
/>
);
}