Как эффективно выбрать из длинного списка вариантов в реагировать на выбор - PullRequest
1 голос
/ 26 апреля 2019

Мой вариант использования - позволить пользователю выбрать тикер из длинного списка из примерно 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}
    />

);
...