Skip to content

Select v6 searchValue is removed when options are updated with onSearch #55968

@aFrameEmilio

Description

@aFrameEmilio

Reproduction link

Edit on CodeSandbox

Steps to reproduce

  1. Create a Select component with showSearch enabled and async options loading
  2. Use onSearch to trigger an API call (debounced) that updates the options prop
  3. Type a search query (e.g., "test")
  4. Wait for the API response to return and update the options
import { useState } from 'react';
import { Select } from 'antd';
import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import debounce from 'lodash-es/debounce';

const queryClient = new QueryClient();

// Simulated API call
const fetchOptions = async (query: string) => {
  await new Promise((resolve) => setTimeout(resolve, 500));
  return [
    { value: '1', label: `Result 1 for "${query}"` },
    { value: '2', label: `Result 2 for "${query}"` },
  ];
};

const AsyncSelect = () => {
  const [searchTerm, setSearchTerm] = useState('');
  const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('');

  const { data: options = [], isFetching } = useQuery({
    queryKey: ['options', debouncedSearchTerm],
    queryFn: () => fetchOptions(debouncedSearchTerm),
    enabled: debouncedSearchTerm.length >= 3,
  });

  const debouncedSetSearch = debounce((value: string) => {
    setDebouncedSearchTerm(value);
  }, 300);

  const handleSearch = (value: string) => {
    setSearchTerm(value);
    debouncedSetSearch(value);
  };

  return (
    <Select
      showSearch
      style={{ width: 300 }}
      placeholder="Search..."
      options={options}
      loading={isFetching}
      onSearch={handleSearch}
      filterOption={false}
    />
  );
};

const App = () => (
  <QueryClientProvider client={queryClient}>
    <AsyncSelect />
  </QueryClientProvider>
);

What is expected?

When the user types "John Doe" and the API returns results, the search input should retain the value "test" and display the filtered options below, with the combobox opened.

What is actually happening?

Intermittently, when the options are updated after the API call returns, the search input value is cleared (reset to empty string). This happens inconsistently - sometimes the search value persists correctly, other times it gets wiped out.

The issue appears to be in rc-select's internal state management. When options update and trigger a re-render, onSearch('') is called internally, which clears the search input, hiding the options in consequence.

Environment Info
antd 6.0.0
React 19.2
System MacOS
Browser Chrome 142.0.7444.176

  • Intermittent nature: The bug doesn't happen 100% of the time, making it harder to reproduce consistently. It seems related to timing/race conditions between the options update and internal state management.
    Root cause investigation: The issue appears to stem from rc-select replacing useMergedState with useControlledState for managing searchValue. The older implementation handled controlled/uncontrolled state more robustly:
// Old (worked correctly):
const [mergedSearchValue, setSearchValue] = useMergedState('', {
  value: searchValue !== undefined ? searchValue : inputValue,
  postState: (search) => search || '',
});

// New (causes the bug):
const [internalSearchValue, setSearchValue] = useControlledState('', searchValue);
  • Workaround: The issue can be worked around by making the search input fully controlled via showSearch={{ searchValue, onSearch }} and guarding against internal empty string resets:
const [searchValue, setSearchValue] = useState('');

const handleSearch = (value: string) => {
  // Guard against rc-select internal resets
  if (value === '' && searchValue.length > 1) {
    return;
  }
  setSearchValue(value);
};

<Select
  showSearch={{ searchValue, onSearch: handleSearch }}
  // ...
/>
  • Environment: antd v6.x, React 19.x

Metadata

Metadata

Assignees

Labels

6.x🐛 BugAnt Design Team had proved that this is a bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions