单击选项后,在 react-select/async 中触发 loadOptions

Trigger loadOptions in react-select/async once option has been click

我有一个异步 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>
);

但是,我想要它,所以当我 select 一个选项时,它会加载另一个列表(没有关闭列表,而是在旧列表位置加载一个新列表),但我不确定我是如何当我点击一个选项时,可以在 react-select/async 内重新触发 loadOptions 道具吗?

一个例子是地址搜索,您在其中搜索街道/post/zip 代码并且有一个选项说明该街道有多少个地址,然后 selecting 该选项然后触发器 loadOptions 进行 API 调用以查找该街道中的所有地址,因此,引入新的选项列表。希望这是有道理的?

如有任何帮助,我们将不胜感激。

我有一个 CodeSandbox 和一个例子。

我尝试了您发布的 Codesandbox link。 onSelect 道具可用于检测用户是否有 selected 任何内容。

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

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

现在我们知道,如果 firstValue 是 selected,我们需要切换选项。所以,按照这个逻辑 -

    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 [];
  };

查看代码框 link 了解更多详情。我在这里和那里调整了一些变量名,这对我来说更有意义。

编辑

当用户select第一个值时重新打开菜单 -

所以,当我们有 firstValue 而不是 secondValue 时,我们可以传递一个 prop(比方说 - retainFocus)-

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

MySelect 中,我们可以在 retainFocus 上添加一个 useEffect,如果作为 true 传递,它只会重新聚焦 select 框。

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

检查更新的代码和框 link

编辑

我还添加了一个在用户输入为空时返回到第一个列表的方法。