Триггер loadOptions в response-select / asyn c после нажатия опции - PullRequest
0 голосов
/ 09 июля 2020

У меня есть асинхронный c react-select компонент

import React, { useRef, useState } from "react";
import Async from "react-select/async";

const MySelect = ({
  label,
  options,
  asyncSelect,
  loadOptions,
  components,
  placeholder,
  fixedHeight,
  onSelect,
  ...props
}) => {
  const ref = useRef();
  const asyncRef = useRef();

  const [fieldValue, setFieldValue] = useState();

  // Gets called when an option is chosen or created.
  const onChange = value => {
    setFieldValue(value);
    if (onSelect) onSelect(value);
  };

  // Toggle menu by presence of input.
  // Reset value if input is emptied.
  const onInputChange = (value, context) => {
    if (!value && context.action === "input-change") {
      onChange(null);
    }
  };

  // Restore input value.
  const onFocus = () => {
    if (fieldValue) {
      asyncRef.current.state.inputValue = fieldValue.label;
      asyncRef.current.select.state.inputValue = fieldValue.label;
      asyncRef.current.handleInputChange(fieldValue.label);
    }
  };

  // Close menu when pressing enter. Submit does not work on mobile.
  const onKeyDown = event => {
    if (event.keyCode === 13) {
      onMenuClose();
    }
  };

  // Blur select element to trigger onFocus on next click.
  const onMenuClose = () => {
    asyncRef.current.select.blur();
  };

  return (
    <>
      <div ref={ref} />
      <Async
        ref={asyncRef}
        {...props}
        value={fieldValue}
        components={components ? components : null}
        loadOptions={loadOptions ? loadOptions : () => null}
        placeholder={placeholder ? placeholder : "Select..."}
        searchPromptText="Start Typing"
        autoload
        isSearchable
        searchable
        cacheOptions
        // Hook into events to make the editing work.
        onChange={onChange}
        onInputChange={onInputChange}
        onFocus={onFocus}
        onKeyDown={onKeyDown}
        onMenuClose={onMenuClose}
      />
    </>
  );
};

export default MySelect;

Я использую этот компонент в другом компоненте для загрузки массива и параметров фильтрации из входного значения

const loadOptions = async inputValue => {
  return new Promise(resolve => resolve(getOptions(inputValue)));
};

const getOptions = async inputValue => {
  // how to trigger this to be called within loadOptions when option is selected?
  if (asyncOptions && asyncOptions.length > 0) {
    const options = asyncOptions.filter(item =>
      item.label.toLowerCase().startsWith(inputValue.trim().toLowerCase())
    );
    if (options && options.length > 0) {
      return options;
    }
    return [];
  }
  await delayWithPromise(1000);
  return [];
};

return (
  <div className="App">
    <MySelect
      name="addressLookup"
      className="addressLookupContainer"
      label="Address Lookup"
      asyncSelect
      components={{
        DropdownIndicator: () => null,
        IndicatorSeparator: () => null,
        NoOptionsMessage: val => <NoOptionsMsg>No Options</NoOptionsMsg>
      }}
      loadOptions={loadOptions}
      onSelect={val => console.log(`${val.value} selected`)}
    />
  </div>
);

Однако я хочу, чтобы, когда я выбираю вариант, он затем загружает другой список (без закрытия списка, вместо этого загружает новый список в место старых списков), но я не уверен, как я могу повторно запустить опору loadOptions внутри react-select/async когда я нажимаю на опцию?

Примером может быть адресный поиск, где вы ищите улицу / почтовый индекс / почтовый индекс, и есть опция, указывающая, сколько адресов с этой улицей, а затем выбирая это option затем вызовет loadOptions вызов API, чтобы найти все адреса на этой улице, таким образом, добавив новый список параметров. Надеюсь, это имеет смысл?

Любая помощь будет принята с благодарностью.

У меня есть CodeSandbox с примером.

1 Ответ

2 голосов
/ 09 июля 2020

У меня было go по ссылке Codesandbox, которую вы разместили. Свойство onSelect может использоваться для определения того, что пользователь выбрал что-либо.

  const [firstValue, setFirstValue] = useState("");
  const [secondValue, setSecondValue] = useState("");

  const onSelect = option => {
    if (firstValue) setSecondValue(option?.value);
    else setFirstValue(option?.value);
  };

Теперь мы знаем, что если выбрано firstValue, нам нужно будет переключить параметры. Итак, по этому логу c -

    const getOptions = async inputValue => {
    const optionChunk = firstValue ? secondOptions : firstOptions;
    if (optionChunk && optionChunk.length > 0) {
      const options = optionChunk.filter(item =>
        item.label.toLowerCase().startsWith(inputValue.trim().toLowerCase())
      );
      if (options && options.length > 0) {
        return options;
      }
      return [];
    }
    await delayWithPromise(1000);
    return [];
  };

Проверьте коды и ссылку для более подробной информации. Я изменил несколько имен переменных здесь и там, что имело для меня больше смысла.

EDIT

Повторное открытие меню, когда пользователь выбирает первое значение -

Итак, когда у нас есть firstValue, а не secondValue, мы можем передать prop (скажем - retainFocus) -

<MySelect
   ... otherProps
   retainFocus={!!firstValue && !secondValue}
/>

In MySelect , мы можем добавить useEffect на retainFocus, который просто перефокусирует поле выбора, если передано как true.

  useEffect(() => {
    if (retainFocus) {
      asyncRef.current.select.focus();
    }
  }, [retainFocus]);

Проверить обновленные коды и ящик ссылка .

РЕДАКТИРОВАТЬ

Я также добавил метод к go обратно к первому списку, когда пользовательский ввод пуст.

...