使用多个钩子去抖动

Debouncing with multiple hooks

我正在尝试在我的应用程序中实现去抖动,但是,我最多只能实现输入速度的去抖动。该应用程序的要点是,它首先从用户那里获取输入,生成一个城市列表,根据输入并选择后,将提供整周的天气预报。

这里是原始代码,没有去抖:

import React, { useState, useEffect } from 'react';
import * as Style from './Searchbar.styles';
import { weatherAPI } from '../API/api';
import endpoints from '../Utils/endpoints';
import { minCharacters } from '../Utils/minChars';
import MultiWeather from './MultiCard';
import useDebounce from '../Hooks/useDebounce';

export default function SearchBar() {
  const [cityName, setCityName] = useState('');
  const [results, setResults] = useState([]);
  const [chooseCityForecast, setChooseCityForecast] = useState([]);

  useEffect(() => {
    if (cityName.length > minCharacters) {
      loadCities();
    }
  }, [cityName, loadCities]);

  //this is used to handle the input from the user
  const cityValueHandler = value => {
    setCityName(value);
  }; 

  //first API call to get list of cities according to user input
  const loadCities = async () => {
    try {
      const res = await weatherAPI.get(endpoints.GET_CITY(cityName));
      setResults(res.data.locations);
    } catch (error) {
      alert(error);
    }
  }; 

  //second API call to get the forecast
  const getCurrentCity = async city => {
    try {
      const res = await weatherAPI.get(endpoints.GET_DAILY_BY_ID(city.id));
      console.log(res);
      setChooseCityForecast(res.data.forecast);
      setCityName('');
      setResults([]);
    } catch (error) {
      alert(error);
    }
  };
  
  return (
    <Style.Container>
      <h1>Search by City</h1>

      <Style.Search>
        <Style.SearchInner>
          <Style.Input type="text" value={cityName} onChange={e => cityValueHandler(e.target.value)} />
        </Style.SearchInner>
        <Style.Dropdown>
          {cityName.length > minCharacters ? (
            <Style.DropdownRow results={results}>
              {results.map(result => (
                <div key={result.id}>
                  <span
                    onClick={() => getCurrentCity(result)}
                  >{`${result.name}, ${result.country}`}</span>
                </div>
              ))}
            </Style.DropdownRow>
          ) : null}
        </Style.Dropdown>
      </Style.Search>
      {chooseCityForecast && (
        <section>
          <MultiWeather data={chooseCityForecast} />
        </section>
      )}
    </Style.Container>
  );
}

除了每次我添加一个额外的字母时创建一个 API 调用之外,上面的代码工作完美。我已经参考了 thread on implementing debouncing。根据我的代码进行调整后,实现如下所示:

  const debounce = (fn, delay) => {
    let timerId;
    return (...args) => {
      clearTimeout(timerId);
      timerId = setTimeout(() => fn(...args), delay);
    }
  };

  const debouncedHandler = useCallback(debounce(cityValueHandler, 200), []);

但是,如前所述,这会导致 debouncing/delaying 用户输入 200 毫秒,同时仍然会创建额外的 API 调用,每个额外的字母都会被调用。

如果我尝试去抖动 loadCities 函数或添加 setTimeout 方法,它会延迟该函数,但仍会在每个附加字母时进行 API 调用。

我有一种预感,我需要重新制作处理输入的逻辑,但在这一点上我没有想法。

经过一番挖掘,我找到了一个简单的解决方案,稍后会重构,但其要点是将 loadCities 函数移动到 use effect 和 implenet useTimeoutclearTimeout useEffect 中的方法并包装 API 调用函数,如下所示:

useEffect(() => {
    let timerID;

    if (inputValue.length > minCharacters) {
      timerID = setTimeout(async () => {
        try {
          const res = await weatherAPI.get(endpoints.GET_CITY(inputValue));
          setResults(res.data.locations);
        } catch (error) {
          alert(error);
        }
      }, 900);
    }
    return () => {
      clearTimeout(timerID);
    };
  }, [inputValue]);

希望这会对某人有所帮助。