React实时搜索Axios请求取消问题求助(附代码)
Hey there! I totally get the frustration—you type a new search term after clearing the old one, but sometimes the old results pop up anyway. That's a classic race condition where an older API request finishes after a newer one and overwrites your state. Let's fix this using Axios's built-in cancellation feature, paired with your existing throttle/debounce setup.
Why This Happens
When you fire off multiple async requests quickly (even with throttle/debounce), there's no guarantee they'll finish in the order you sent them. If an older request takes longer to resolve than a newer one, its response will land last and overwrite your state with outdated data.
Step-by-Step Solution
1. Update Your API Service to Support Cancellation
First, modify your apiGet function to accept an Axios CancelToken. This lets us cancel in-flight requests when a new search is triggered:
import axios from 'axios'; export const apiGet = async (path = '', args = {}, cancelToken) => { const endpoint = "/aaa/aaa/aaa"; try { return axios.get(endpoint, { ...getConfig(), // Keep your existing config cancelToken: cancelToken }); } catch (e) { // Ignore cancellation errors to avoid cluttering the console if (axios.isCancel(e)) { console.log('Old search request canceled:', e.message); } else { // Re-throw other errors for proper handling throw e; } } };
2. Add Cancellation Logic to Your Action Creator
We'll maintain a reference to the current cancel token source. Every time a new search is triggered, we cancel the previous request (if it's still running) before sending the new one:
import axios from 'axios'; // Track the current cancel token source across search calls let cancelTokenSource = null; export function getSearch(value) { return (dispatch) => { // Cancel any pending old request first if (cancelTokenSource) { cancelTokenSource.cancel('Canceling outdated search request'); } dispatch({ type: GETTING_DATA }); const arg = { search: value }; // Create a new cancel token source for this request cancelTokenSource = axios.CancelToken.source(); apiGet('abc', arg, cancelTokenSource.token) .then(response => getResults(dispatch, response)) .catch(error => { // Only handle non-cancellation errors if (!axios.isCancel(error)) { console.error('Search request failed:', error); // Optional: Dispatch an error action here to update state // dispatch({ type: SEARCH_ERROR, payload: error }); } }); }; } const getResults = (dispatch, response) => { dispatch({ type: SEARCH_DATA, payload: response.data }); };
3. Ensure Throttle/Debounce Works Correctly (Component Side)
If you're using a functional component, make sure to wrap your search handler with useCallback to preserve the function reference—this ensures your throttle/debounce doesn't reset on every render:
import { useCallback } from 'react'; import { throttle } from 'throttle-debounce'; import { useDispatch } from 'react-redux'; import { getSearch } from './your-actions-file'; function SearchComponent() { const dispatch = useDispatch(); const handleSearch = useCallback(throttle((value) => { dispatch(getSearch(value)); }, 300), [dispatch]); // Dependencies array ensures stability return ( <input type="text" onChange={(e) => handleSearch(e.target.value)} placeholder="Search..." /> ); }
Quick Note on Your Reducer
I noticed a small typo in your reducer: GETTING_DATA updates state.results (lowercase) while SEARCH_DATA updates state.Results (uppercase). This will cause inconsistent state updates—make sure the property names match!
switch (action.type) { case GETTING_DATA: return { ...state, results: action.payload }; // Or change to Results, just be consistent case SEARCH_DATA: return { ...state, results: action.payload }; // Match the property name here }
How This Works
- Every time a new search is triggered, we cancel any unfinished old request using the cancel token.
- Cancelled requests won't reach the
.thenblock, so they can't overwrite your state with old data. - The throttle/debounce ensures we don't flood the API with too many requests, while the cancellation handles the remaining race condition.
内容的提问来源于stack exchange,提问作者monkeyjs

