使用退格键清除输入字段时显示最后结果的搜索组件

Search component showing last result when input field is cleared using backspace

所以我正在使用 Next.js 并构建了一个基本的搜索页面,其中包含输入并将查询后的结果存储在状态数组上。问题是当我使用退格键快速清除输入字段时,它显示了最后一个关键字的结果。

我认为我以错误的方式使用了 React 状态。

以下是我使用美力搜索查询搜索的方式:

const [search, setSearch] = useState([]);
const [showSearch, setShowSearch] = useState(false);

async function onChange(e) {
    if (e.length > 0) {

      await client.index('sections')
        .search(e)
        .then((res) => {
          const list = res.hits.map((elm) => ({
            title: elm.Title,
            content: elm.formattedContent
          }));

          setSearch(list);
          setShowSearch(true);
        });
    } else {
      setSearch([]);
      setShowSearch(false);
    }
  }

这是输入字段和搜索结果:

<div className="searchPage wrapper">
        <input
          type="text"
          aria-label="Search through site content"
          placeholder="Search your keywords"
          onChange={(e) => onChange(e.target.value);}
        />

        {showSearch && (
          <div className="searchPageResults">
            <p className="suggested">Top Results</p>
            {search.length > 0 ? (
              <ul>
                {search.map((item, index) => (
                  <li key={`search-${index}`}>
                    <Link href={`/${item.slug}`}>
                      <a role="link">
                        {item.title}
                      </a>
                    </Link>

                    <p>{item.content}</p>
                  </li>
                ))}
              </ul>
            ) : (
              <p className="noResults">No results found</p>
            )}
          </div>
        )}
      </div>

防止这种情况的最佳做法是什么?

您可以在此处查看实时实施:https://budgetbasics.openbudgetsindia.org/search

要重现问题:

问题

我怀疑当你快速退格时,最后一个用“b”发出的请求异步解析最后一个onChange 调用是在 e.length > 0 为假的地方进行的。 search 状态更新为空数组,一旦最终异步请求解决,search 状态就会更新为“b”的结果。

解决方案

一个可能的解决方案是去除 onChange 处理程序的抖动,这样就不会为快速打字者发出无用的请求。 debounce 来自 lodash 是一个常用的实用程序。我使用了 300ms 的延迟,但这显然是可以调整的,以满足您的需求以及对您或您的一般用户来说最合适的感觉。

import debounce from 'lodash/debounce';

async function onChange(e) {
  if (e.length > 0) {
    await client.index('sections')
      .search(e)
      .then((res) => {
        const list = res.hits.map((elm) => ({
          title: elm.Title,
          content: elm.formattedContent
        }));

        setSearch(list);
        setShowSearch(true);
      });
  } else {
    setSearch([]);
    setShowSearch(false);
  }
}

const debouncedOnChange = useMemo(() => debounce(onChange, 300), []);

...

<input
  type="text"
  aria-label="Search through site content"
  placeholder="Search your keywords"
  onChange={(e) => debouncedOnChange(e.target.value)} // <-- use debounced handler
/>