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

React中如何配置多个useEffect Hook以避免重复请求并在查询变更时重置页码

解决React中多useEffect触发重复请求及查询重置页码问题

我来帮你搞定这个问题!你现在遇到的核心痛点有两个:一是父组件传来的query变化时,需要自动把页码重置回1;二是避免因query和page先后变动导致的重复请求。咱们一步步来优化你的代码:

第一步:拆分useEffect,单独处理查询条件变化时的页码重置

首先,我们单独写一个useEffect来监听query的变化——只要query更新,就把page重置为初始值1。这样就能保证每次新查询都是从第一页开始,不会停留在之前的页码:

useEffect(() => {
  // 只有当前页码不是1的时候才重置,避免无意义的状态更新
  if (page !== 1) {
    setPage(1);
  }
}, [query]);

第二步:优化请求逻辑,用请求取消避免重复请求和竞态问题

之前的问题在于,当query变化时,会先触发一次请求,然后page重置又触发一次请求,导致重复调用。另外,还存在竞态风险:如果前一个请求还没返回,新的请求结果先回来了,旧请求的结果可能会覆盖新的,导致页面显示错误数据。

我们可以用Axios的取消令牌来解决这个问题,同时调整请求触发的useEffect:

// 先导入useRef
import React, { useState, useEffect, useRef } from "react"

// 在组件内部添加一个ref来存储取消令牌
const cancelTokenRef = useRef(null);

// 处理请求的useEffect
useEffect(() => {
  // 如果有未完成的请求,先取消它,避免重复请求和竞态
  if (cancelTokenRef.current) {
    cancelTokenRef.current.cancel('请求已取消:参数发生变化');
  }
  // 创建新的取消令牌
  cancelTokenRef.current = axios.CancelToken.source();

  // 把results改成异步函数,更符合现代写法
  const fetchResults = async () => {
    setIsLoaded(false);
    setError(null);
    try {
      const response = await axios.get(dataUrl, {
        params: {
          query: query,
          page: page
        },
        // 把取消令牌传给当前请求
        cancelToken: cancelTokenRef.current.token
      });
      setItems(response.data);
    } catch (err) {
      // 忽略取消请求的错误,只处理真正的请求失败
      if (!axios.isCancel(err)) {
        setError(err.message);
      }
    } finally {
      setIsLoaded(true);
    }
  };

  fetchResults();

  // 组件卸载或者依赖变化时,清理未完成的请求
  return () => {
    if (cancelTokenRef.current) {
      cancelTokenRef.current.cancel('请求已取消:组件卸载或参数变化');
    }
  };
}, [query, page]);

最终的完整代码调整

把这些整合到你的组件里,最终代码大概是这样的:

import React, { useState, useEffect, useRef } from "react"
import axios from 'axios'
import { Link, useLocation } from "react-router-dom";

export default function DocumentList({query}) {
  const [page, setPage] = useState(1);
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);
  const dataUrl = 'https://api/test';
  const cancelTokenRef = useRef(null);

  // 监听query变化,重置页码
  useEffect(() => {
    if (page !== 1) {
      setPage(1);
    }
  }, [query]);

  // 触发请求并处理取消逻辑
  useEffect(() => {
    if (cancelTokenRef.current) {
      cancelTokenRef.current.cancel('请求已取消:参数发生变化');
    }
    cancelTokenRef.current = axios.CancelToken.source();

    const fetchResults = async () => {
      setIsLoaded(false);
      setError(null);
      try {
        const response = await axios.get(dataUrl, {
          params: { query, page },
          cancelToken: cancelTokenRef.current.token
        });
        setItems(response.data);
      } catch (err) {
        if (!axios.isCancel(err)) {
          setError(err.message);
        }
      } finally {
        setIsLoaded(true);
      }
    };

    fetchResults();

    return () => {
      if (cancelTokenRef.current) {
        cancelTokenRef.current.cancel('请求已取消:组件卸载或参数变化');
      }
    };
  }, [query, page]);

  const handlePrevPage = () => {
    if (page > 1) {
      setPage(page - 1);
    }
  };

  const handleNextPage = () => {
    setPage(page + 1);
  };

  function Pagination() {
    return (
      <div>
        <button onClick={handlePrevPage}> Prev </button>
        <span className="block py-1 px-2 font-bold text-white">{page}</span>
        <button onClick={handleNextPage} > Next </button>
      </div>
    );
  }

  function List() {
    // 这里写你的列表渲染逻辑,比如遍历items
    return (
      <ul>
        {items.map(item => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    );
  }

  return (
    <div>
      {error && <div className="text-red-500">Error: {error}</div>}
      {!isLoaded && <div className="text-gray-500">Loading...</div>}
      {isLoaded && <List />}
      <Pagination />
    </div>
  );
}

关键要点总结

  • 拆分职责:用两个useEffect分别处理页码重置和请求触发,逻辑更清晰,避免耦合。
  • 取消冗余请求:通过Axios的取消令牌,每次触发新请求前取消旧的未完成请求,既减少了不必要的API调用,又避免了竞态问题。
  • 避免无意义更新:重置页码时加了page !== 1的判断,防止query不变但页码本来就是1时的状态更新。

这样调整后,你就能实现:query变化时自动回到第一页,同时不会出现重复请求的问题啦!

内容的提问来源于stack exchange,提问作者demogorgorn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 02:52:37