Компонент поиска, показывающий последний результат при очистке поля ввода с помощью backspace

#reactjs #search #next.js #meilisearch

Вопрос:

Поэтому я использую Next.js и построил базовую страницу поиска с вводом и сохранением результатов после запроса в массиве состояний. Проблема в том, что когда я быстро очищаю поле ввода с помощью backspace, оно показывает результат по последнему ключевому слову.

Я считаю, что неправильно использую состояние Реакции.

Вот как я запрашиваю поиск с помощью meilisearch:

 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 amp;amp; (
          <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

Чтобы воспроизвести проблему:

  • Поищите что-нибудь, например: Budget
  • После отображения результатов удерживайте клавишу backspace, когда поле будет очищено, результаты поиска будут показаны для b
  • Проблемы не возникнет, если я выделю весь текст в поле и удалю его с помощью backspace.

Ответ №1:

Вопрос

Я подозреваю, что, когда вы быстро возвращаетесь назад, последний запрос, выполненный с помощью «b», асинхронно разрешается после выполнения последнего onChange вызова, где e.length > 0 значение false. search Состояние обновляется до пустого массива, и как только окончательный асинхронный запрос разрешится search , состояние обновляется с результатом «b».

Решение

Одним из возможных решений было бы отменить onChange обработчик, чтобы бесполезные запросы не выполнялись для быстрых наборщиков. списание с 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
/>
 

Комментарии:

1. кстати, useMemo используется только для оптимизации, верно?

2. @ShoaibAhmed Да. Поскольку это функциональный компонент, вы обычно не хотите повторно объявлять отмененный обратный вызов при каждом рендеринге. Я попробовал это в codesandbox без useMemo крючка, и это все еще работало, но я подозреваю, что это было только потому, что ввод является неконтролируемым вводом.