React.js - 自动完成:如何在单击其他区域时隐藏建议列表

React.js - Autocomplete: How To Hide Suggest Lst While Clicking On Other Areas

我读了这个 tutorial 并尝试制作一个自动完成的文本框。

部分代码如下:

import React, {useState} from 'react';
import {SearchInput, SearchUL, SearchLI} from './index.style';

// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({suggestions = []}) => {
  const [activeSuggestion, setActiveSuggestion] = useState(0);
  const [filteredSuggestions, setFilteredSuggestions] = useState([]);
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [userInput, setUserInput] = useState('');
  const [isTyping, setIsTyping] = useState(false);

  // timer id
  let typingTimer;
  //
  let doneTypingInterval = 1000;

  // on change
  const onChange = e => {
    // input
    const userInput = e.currentTarget.value;

    // match
    const filteredSuggestions = suggestions.filter(
      suggestion =>
        suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
    );

    // set state
    setActiveSuggestion(0);
    setFilteredSuggestions(filteredSuggestions);
    setShowSuggestions(true);
    setUserInput(e.currentTarget.value);
  };

  // onclick
  const onClick = e => {
    // set state
    setActiveSuggestion(0);
    setFilteredSuggestions([]);
    setShowSuggestions(false);
    setUserInput(e.currentTarget.innerText); //?

    console.log('on click');
  };

  // done
  const doneTyping = () => {
    console.log('done type');
    setIsTyping(false);
  };

  // key down
  const onKeyDown = e => {
    if (e.keyCode === 13) {
      // 1. enter key
      // state
      setActiveSuggestion(0);
      setShowSuggestions(false);
      setUserInput(filteredSuggestions[activeSuggestion]);
    } else if (e.keyCode === 38) {
      // 2. up arrow key
      // no active, out
      if (activeSuggestion === 0) {
        return;
      }

      // go up, -1
      setActiveSuggestion(activeSuggestion - 1);
    } else if (e.keyCode === 40) {
      // 3. down arr
      if (activeSuggestion - 1 === filteredSuggestions.length) {
        return;
      }

      // go down +1
      setActiveSuggestion(activeSuggestion + 1);
    } else {
    }
  };

  // key up
  const onKeyUp = e => {
    // clear old timer
    clearTimeout(typingTimer);

    // has val, then clean up
    if (e.currentTarget.value) {
      setIsTyping(true);
      typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
  };

  // tmp: sugList
  let suggestionsListComponent;
  // state: show and u-input
  if (showSuggestions && userInput) {
    // state: filterSug.len
    if (filteredSuggestions.length) {
      // ul + li
      suggestionsListComponent = (
        <SearchUL>
          {filteredSuggestions.map((suggestion, index) => {
            return (
              <SearchLI key={suggestion} onClick={onClick}>
                {suggestion}
              </SearchLI>
            );
          })}
        </SearchUL>
      );
    } else {
      // no
      suggestionsListComponent = (
        <div>
          <em>No suggestions!</em>
        </div>
      );
    }
  }

  // --------
  // NOTE: this makes the drop down list disappear, but not able to click the list
  // --------
  const onBlur = () => {
    setShowSuggestions(false);
  };

  return (
    <>
      <SearchInput
        type="text"
        onChange={onChange}
        onKeyDown={onKeyDown}
        onKeyUp={onKeyUp}
        value={userInput}
        isTyping={isTyping}
        //onBlur={onBlur}
      />
      {suggestionsListComponent}
    </>
  );
};

export default Autocomplete;

抱歉,代理不允许我上传图片。总之,当你开始输入时,如果匹配,就会有一个下拉列表。单击项目中的 1 个,搜索框将被填满。

现在图片打到一半,然后鼠标移动到其他区域,所以像onBlur,下拉列表无法消失。

我试着做了一个onBlur函数

const onBlur = () => {
    setShowSuggestions(false);
  };

并有这样的东西:

return (
    <>
      <SearchInput
        type="text"
        onChange={onChange}
        onKeyDown={onKeyDown}
        onKeyUp={onKeyUp}
        value={userInput}
        isTyping={isTyping}
        //onBlur={onBlur}
      />
      {suggestionsListComponent}
    </>
  );

这一次,如果我点击其他区域,下拉列表可以隐藏,但是当我正常输入和select下拉item时,selected 项目无法进入搜索框。

完整代码here

我已经为您创建了一个沙盒 link。检查这个:https://codesandbox.io/s/testformik-xgl3w

在此,我用 div 包装了您的组件并传递了 ref。在 componentDidMount 上,我正在收听单击 document 并调用该函数,如果单击的目标与自动完成组件(输入和建议)不同,我将关闭建议框。

这是新代码:

import React, { useState, useEffect } from "react";
import { SearchInput, SearchUL, SearchLI } from "./index.style";

const inputRef = React.createRef();
// based on this: https://programmingwithmosh.com/react/simple-react-autocomplete-component/
const Autocomplete = ({ suggestions = [] }) => {
  const [activeSuggestion, setActiveSuggestion] = useState(0);
  const [filteredSuggestions, setFilteredSuggestions] = useState([]);
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [userInput, setUserInput] = useState("");
  const [isTyping, setIsTyping] = useState(false);

  const handleOuterClick = e => {
    if (!inputRef.current.contains(e.target)) {
      setShowSuggestions(false);
    }
  };

  useEffect(() => {
    document.addEventListener("click", handleOuterClick);
  }, []);

  // timer id
  let typingTimer;
  //
  let doneTypingInterval = 1000;

  // on change
  const onChange = e => {
    // input
    const userInput = e.currentTarget.value;

    // match
    const filteredSuggestions = suggestions.filter(
      suggestion =>
        suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
    );

    // set state
    setActiveSuggestion(0);
    setFilteredSuggestions(filteredSuggestions);
    setShowSuggestions(true);
    setUserInput(e.currentTarget.value);
  };

  // onclick
  const onClick = e => {
    // set state
    setActiveSuggestion(0);
    setFilteredSuggestions([]);
    setShowSuggestions(false);
    setUserInput(e.currentTarget.innerText); //?

    console.log("on click");
  };

  // done
  const doneTyping = () => {
    console.log("done type");
    setIsTyping(false);
  };

  // key down
  const onKeyDown = e => {
    if (e.keyCode === 13) {
      // 1. enter key
      // state
      setActiveSuggestion(0);
      setShowSuggestions(false);
      setUserInput(filteredSuggestions[activeSuggestion]);
    } else if (e.keyCode === 38) {
      // 2. up arrow key
      // no active, out
      if (activeSuggestion === 0) {
        return;
      }

      // go up, -1
      setActiveSuggestion(activeSuggestion - 1);
    } else if (e.keyCode === 40) {
      // 3. down arr
      if (activeSuggestion - 1 === filteredSuggestions.length) {
        return;
      }

      // go down +1
      setActiveSuggestion(activeSuggestion + 1);
    } else {
    }
  };

  // key up
  const onKeyUp = e => {
    // clear old timer
    clearTimeout(typingTimer);

    // has val, then clean up
    if (e.currentTarget.value) {
      setIsTyping(true);
      typingTimer = setTimeout(doneTyping, doneTypingInterval);
    }
  };

  // tmp: sugList
  let suggestionsListComponent;
  // state: show and u-input
  if (showSuggestions && userInput) {
    // state: filterSug.len
    if (filteredSuggestions.length) {
      // ul + li
      suggestionsListComponent = (
        <SearchUL>
          {filteredSuggestions.map((suggestion, index) => {
            return (
              <SearchLI key={suggestion} onClick={onClick}>
                {suggestion}
              </SearchLI>
            );
          })}
        </SearchUL>
      );
    } else {
      // no
      suggestionsListComponent = (
        <div>
          <em>No suggestions!</em>
        </div>
      );
    }
  }

  return (
    <div ref={inputRef}>
      <SearchInput
        type="text"
        onChange={onChange}
        onKeyDown={onKeyDown}
        onKeyUp={onKeyUp}
        value={userInput}
        isTyping={isTyping}
      />
      {suggestionsListComponent}
    </div>
  );
};

export default Autocomplete;

希望对您有所帮助!

使用计时器更新您的 onBlur 函数,如下所示:

const onBlurr = () => {
   setTimeout(() => setShowSuggestions(false), 500);
};