使用多个钩子去抖动
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 useTimeout
和 clearTimeout
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]);
希望这会对某人有所帮助。
我正在尝试在我的应用程序中实现去抖动,但是,我最多只能实现输入速度的去抖动。该应用程序的要点是,它首先从用户那里获取输入,生成一个城市列表,根据输入并选择后,将提供整周的天气预报。
这里是原始代码,没有去抖:
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 调用之外,上面的代码工作完美。我已经参考了
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 useTimeout
和 clearTimeout
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]);
希望这会对某人有所帮助。