【问题标题】:Custom autocomplete validation in ReactReact 中的自定义自动完成验证
【发布时间】:2021-07-02 18:42:51
【问题描述】:

我创建了一个自定义自动完成下拉列表,但我需要强制用户从列表中选择一些内容,同时保持在输入字段中输入内容的可能性。

如何创建这样的验证?自动完成组件是一个列表,可以从 API 中选择一个东西,但用户仍然可以在输入字段中输入任何内容并发送它。我希望用户只能使用建议的项目提交表单。你能帮我解决这个问题吗?

基本上问题是:如何检查输入值是否与从自动完成组件中获取的项目相同?

export type AutocompletePropsType = {
  inputValue: string;
  currentIndex: number;
  onSelect: (selectedItem: string, slug?: string | null) => void;
  hasError: boolean;
  list: AutocompleteResponse['data'];
};

type PropsType = {
  onChange: (value: string, type: string, isValid?: boolean, slug?: string) => void;
  inputName: string;
  type: InputType;
  label: string;
  autocompleteComponent: React.ComponentType<AutocompletePropsType>;
  validator?: UseFieldValidatorType;
};

const SearchInput = (props: PropsType): JSX.Element => {
  const { onChange, inputName, type, label, autocompleteComponent: AutocompleteComponent, validator } = props;
  const autocompleteRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const classes = useStyles();
  const router = useRouter();

  const dispatch = useDispatch();
  const [currentIdx, setCurrentIdx] = useState(-1);

  const [state, { handleInputBlur, handleInputChange, handleInputFocus }] = useField({
    inputValidator: validator,
  });

  const { isFocused, isValid, value } = state;

  const [list, { hasError }] = useAutocomplete<AutocompleteResponse['data']>(value, async (query: string) =>
    searchApi.autocomplete[type](query).then((res) => (res.data as unknown) as AutocompleteResponse['data']),
  );

  const handleSelect = (alias: string): void => {
    handleInputChange(alias);
    onChange(alias, type);
    handleInputBlur();
  };

  const handleFocus = (): void => {
    handleInputFocus();
    inputRef.current.focus();
  };

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    handleInputChange(event);
    const isValueValid = validator ? validator(event.target.value).success : true;

    onChange(event.target.value, event.target.name, isValueValid);
  };

  useClickOutside(autocompleteRef, () => {
    handleInputBlur();
  });

  useEffect(() => {
    (async (): Promise<void> => {
      if (router.pathname !== '/' && !value) {
        const { alias, slug } = await getInitialInputValuesFromQuery(router, type);

        handleInputChange(alias);
        onChange(alias, type, isValid, slug);
      }
    })();
  }, []);

  return (
    <div className={classes['search-input']} ref={autocompleteRef}>
      <div onClick={handleFocus}>
        <label
          className={cx(classes['search-input__input__label'], {
            [classes['search-input__input__label--active']]: isFocused || !!value,
          })}
        >
          {label}
        </label>
        <input
          id={inputName}
          onChange={handleChange}
          onFocus={handleInputFocus}
          name={inputName}
          value={value}
          className={classes['search-input__input__field']}
          autoComplete="off"
          checked={false}
          ref={inputRef}
          maxLength={25}
        />
        <div
          className={cx(classes['input__decorator'], {
            [classes['input__decorator--focused']]: isFocused,
          })}
        />
      </div>
      {transitions(
        ({ transform, opacity }, item) =>
          item && (
            <animated.div className={classes['search-input__dropdown']} style={{ opacity: opacity as any, transform }}>
              {value.length > 2 && (
                <AutocompleteComponent
                  onSelect={handleSelect}
                  inputValue={value}
                  currentIndex={currentIdx}
                  hasError={hasError}
                  list={list}
                />
              )}
            </animated.div>
          ),
      )}
    </div>
  );
};

export default SearchInput;

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    免责声明:我没有完全阅读代码,因为其中有一些我还不明白的东西(打字稿)。但考虑到这一点:

    自动完成组件是一个列表

    如何检查输入值是否与从自动完成中获取的项目相同

    在发送表单之前,您可以使用Array.prototype.includes() 将用户输入与您的列表相匹配。

    let userInput = "foo"
    let userInput2 = "opsIAmInvalid"
    
    let apiList = ["foo", "doo", "boo"]
    
    apiList.includes(userInput) // true
    apiList.includes(userInput2) // false
    

    另一种广泛的方法可以使用更复杂的组件来完成。两个想法:

    1. 两个输入,一个是过滤列表的用户文本,另一个是从列表中选择的实际项目。发送表单时忽略的第一个,您可以使用一些焦点验证来更改样式。 (我不喜欢这个主意 :x)
    2. 一个输入,您的用户键入任何过滤列表的内容。当他们从列表中单击某些内容时,您将其保存为变量/状态。您忽略提交时的输入,只需验证变量/状态是否存在。

    【讨论】:

      猜你喜欢
      • 2015-08-14
      • 2018-08-05
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      相关资源
      最近更新 更多