Мой вариант использования - позволить пользователю выбрать тикер из длинного списка из примерно 8000 компаний. Я выбираю все компании, когда компонент монтируется, поэтому мне не нужна асинхронная функция реагирования-выбора. Проблема действительно заключается в отображении и прокрутке 8000 пунктов (как описано в нескольких открытых выпусках, таких как this ).
Я думаю, зачем отображать 8000 записей, когда пользователь не может ничего сделать с таким большим списком. Вместо этого почему бы не показать максимум 5 матчей. По мере того, как пользователь набирает больше, совпадения продолжают улучшаться. В частности:
- Когда ввод пуст, не показывать опции
- Если введен один символ, все равно будут сотни совпадений, но показаны только первые 5
- Поскольку пользователь продолжает печатать, количество совпадений будет уменьшаться, но все равно будет ограничено до 5. Однако они будут более релевантными.
Я не вижу, чтобы это решение упоминалось где-либо, поэтому мне было интересно, имеет ли оно смысл. Также хотел выяснить, каков наилучший способ реализовать это с реагированием на выбор. Я попробовал следующие два подхода - можете ли вы придумать лучший способ:
Подход 1. Использование асинхронной реакции выбора
Хотя мне не нужна асинхронная выборка, я могу использовать эту функцию для фильтрации параметров. Кажется, работает очень хорошо:
const filterCompanies = (value: string) => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
let count = 0;
return inputLength === 0
? []
: companies.filter(company => {
const keep =
count < 5 &&
(company.ticker.toLowerCase().indexOf(inputValue) >= 0 ||
company.name.toLowerCase().indexOf(inputValue) >= 0);
if (keep) {
count += 1;
}
return keep;
});
};
const promiseOptions = (inputValue: string) =>
Promise.resolve(filterCompanies(inputValue));
return (
<AsyncSelect<Company>
loadOptions={promiseOptions}
value={selectedCompany}
getOptionLabel={option => `${option.ticker} - ${option.name}`}
getOptionValue={option => option.ticker}
isClearable={true}
isSearchable={true}
onChange={handleChange}
/>
);
Подход 2. Использование filterOption
Здесь я использую filterOption
для прямой фильтрации по списку. Однако это не очень хорошо работает - функция filterOption очень близорука - она получает только один вариант-кандидат за раз и должна решить, соответствует ли он или нет. Используя этот подход, я не могу сказать, переступил ли я предел показа 5 вариантов или нет. Чистый результат: при пустом вводе я показываю все 8000 вариантов, поскольку пользователь начинает печатать, количество вариантов уменьшается, но все еще довольно велико - так что вялость все еще есть. Я бы подумал, что filterOption
будет более прямым подходом для моего варианта использования, но оказывается, что он не так хорош, как асинхронный подход. Я что-то упустил?
const filterOption = (candidate: Option, input: string) => {
const { ticker, name } = candidate.data;
const inputVal = input.toLowerCase();
return (
ticker.toLowerCase().indexOf(inputVal) >= 0 ||
name.toLowerCase().indexOf(inputVal) >= 0
);
};
return (
<ReactSelect
options={companies}
value={selectedCompany}
filterOption={filterOption}
getOptionLabel={option => `${option.ticker} - ${option.name}`}
getOptionValue={option => option.ticker}
isClearable={true}
isSearchable={true}
onChange={handleChange}
/>
);