我正试图在我的React应用程序中添加去抖动功能,并且希望在没有诸如loadash,第三方节点模块等库的情况下进行。我尝试了一些帖子,但对我没有任何帮助。
基本上,在handleSearch
中,我只是调度了redux操作,该操作对API端点执行查询,并在输入绑定值中对redux状态的pice进行调用,并调用handleSearch onChange。
我的代码是:
const handleSearch = (e: React.FormEvent<HTMLInputElement>) => {
const value = e.currentTarget.value
dispatch(setSearch(value))
}
然后返回
<input type="text" value={searchQuery} onChange={handleSearch} />
另外,我的动作:
export const searchMovies = (category: String, searchQuery: string) => async (dispatch: Dispatch<ControlDispatchTypes>) => {
try {
dispatch({
type: SEARCH_LIST_LOADING
})
let res: any;
if (searchQuery.length >= 3) {
res = await axios.get(`https://api.themoviedb.org/3/search/${category}?api_key=xxxxxxxxxx&query=${searchQuery}`)
}
dispatch({
type: SEARCH_LIST_SUCCESS,
payload: res.data.results
})
} catch (error) {
dispatch({
type: SEARCH_LIST_FAIL
})
}
}
和用于搜索的减速器
...
case SET_SEARCH:
return {
...state,
search: action.payload
}
case SEARCH_LIST_LOADING:
return {
...state,
searchLoading: false
}
case SEARCH_LIST_SUCCESS:
return {
...state,
searchLoading: false,
items: action.payload
}
case SEARCH_LIST_FAIL:
return {
...state,
searchLoading: false
}
...
答案 0 :(得分:0)
我已经看到React Redux项目使用此资源为项目添加去抖动功能。它取自https://davidwalsh.name/javascript-debounce-function
export const debounce = (func, wait, immediate) => {
let timeout;
return function () {
const later = () => {
timeout = null;
if (!immediate) func.apply(this, arguments);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(this, arguments);
};
};
答案 1 :(得分:0)
尚不清楚您如何调用searchMovies()
,但我认为,执行此操作的最简单方法是将调度程序包装在已被反跳的回调中。
const debounce = (fn, delay) => {
let timeout = -1;
return (...args) => {
if (timeout !== -1) {
clearTimeout(timeout);
}
timeout = setTimeout(fn, delay, ...args);
};
};
export const App = () => {
const query = useSelector(selectQuery);
const dispatch = useDispatch();
const requestMovies = useMemo(() => {
return debounce((query) => {
dispatch(searchMovies(query))
}, 300);
}, []);
const onQueryChange = useCallback((q) => {
dispatch(setSearch(q));
requestMovies(q);
}, []);
return (
<div className="App">
<input value={query} onChange={(e) => onQueryChange(e.currentTarget.value)} />
</div>
);
}
那是简单的解决方案。如果您想获得加分,请考虑编写一个自定义钩子以包含所有状态和逻辑。它可以清理组件并使电影搜索可重复使用。
const QUERY_DEBOUNCE_PERIOD = 400;
const useMovieSearch = () => {
const dispatch = useDispatch();
const query = useSelector(selectQuery);
const category= useSelector(selectCategory);
const fence = useRef("");
const requestMovies = useMemo(() => {
const request = async (category, query) => {
const uri = `https://api.themoviedb.org/3/search/${category}?api_key=xxxxxxxxxx&query=${query}`;
fence.current = uri;
dispatch({
type: SEARCH_LIST_LOADING
});
try {
if (query.length >= 3) {
const res = await axios.get(uri);
if (fence.current === uri) {
dispatch({
type: SEARCH_LIST_SUCCESS,
payload: res.data.results
});
}
}
} catch (error) {
dispatch({
type: SEARCH_LIST_FAIL
})
}
};
return debounce(request, QUERY_DEBOUNCE_PERIOD);
}, []);
const searchMovies = useCallback((category, query) => {
dispatch({ type: SET_SEARCH, payload: query });
requestMovies(category, query);
}, [requestMovies]);
return {
query,
category,
searchMovies
};
};
这看起来应该很标准。我只是将所有searchMovies
逻辑移到了一个自定义钩子中。我还添加了围栏。由于Internet的异步性,因此不能保证您以发送请求的顺序获得结果。这只会忽略除最近的请求以外的所有响应。
用法几乎就是您所期望的。
const SearchBar = () => {
const [query, category, searchMovies] = useMovieSearch();
return <input value={query} onChange={(e) => searchMovies(category, e.currentTarget.value)} />;
};