You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React实时搜索Axios请求取消问题求助(附代码)

Fixing Race Conditions in React Real-Time Search with Axios & Throttle/Debounce

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 .then block, 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:06