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

React中useEffect因refresh函数重复创建引发无限循环的解决咨询

解决useEffect无限循环的方案

方案一:用useRef缓存refresh函数

既然不想在useRefresh里加useCallback,可以在组件内部用useRef把refresh函数存起来,每次组件渲染时更新ref的current值,但不会触发useEffect的依赖变化,彻底切断循环触发的源头。具体代码修改如下:

import { useRef, useEffect } from 'react';

// ... 其他组件代码

const refresh = useRefresh();
const refreshRef = useRef(refresh);

// 每次渲染更新refreshRef,确保拿到最新的refresh函数
useEffect(() => {
  refreshRef.current = refresh;
}, [refresh]);

useEffect(() => {
  const handleRefresh = async () => {
    if (!tokenExpired || !isOpen) return;
    // 调用缓存的refresh函数
    const token = await refreshRef.current();
    tokenRef.current = token;
  };

  const fetchStories = async () => {
    if (isOpen === false) return;

    try {
      const result = await axiosPrivate.get(`${BASE_URL}/api/users/stories/${userId}`, {
        headers: { "x-auth-token": tokenRef.current },
      });
      dispatch(setUserStories({ userId, stories: result?.data?.stories }));
    } catch (err) {
      console.log(err);
      return;
    }
  };

  const handleMount = async () => {
    await handleRefresh();
    await fetchStories();
    setIsLoading(false);
  };

  handleMount();
}, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]); // 不再将refresh加入依赖数组

方案二:合理使用ESLint忽略注释(谨慎使用)

如果你能确定refresh函数的内部逻辑不会随组件渲染发生变化(比如它没有依赖外部可变状态),可以直接在useEffect的依赖数组后添加ESLint忽略注释,跳过对refresh的依赖检查:

useEffect(() => {
  // ... 内部逻辑保持不变
  handleMount();
}, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]); // eslint-disable-next-line react-hooks/exhaustive-deps

注意:如果后续useRefresh内部的refresh函数依赖了可变值,这个方法会导致调用到旧的refresh函数,引发潜在bug,仅适合逻辑稳定的场景。

方案三:将异步逻辑封装到自定义hook

把挂载时的刷新+请求逻辑抽离成独立的自定义hook,内部集中处理refresh的缓存问题,让组件代码更简洁:

import { useState, useRef, useEffect } from 'react';

function useFetchUserStories(isOpen, userId, tokenExpired, dispatch, axiosPrivate) {
  const [isLoading, setIsLoading] = useState(true);
  const refresh = useRefresh();
  const refreshRef = useRef(refresh);
  const tokenRef = useRef();

  useEffect(() => {
    refreshRef.current = refresh;
  }, [refresh]);

  useEffect(() => {
    const handleRefresh = async () => {
      if (!tokenExpired || !isOpen) return;
      const token = await refreshRef.current();
      tokenRef.current = token;
    };

    const fetchStories = async () => {
      if (isOpen === false) return;

      try {
        const result = await axiosPrivate.get(`${BASE_URL}/api/users/stories/${userId}`, {
          headers: { "x-auth-token": tokenRef.current },
        });
        dispatch(setUserStories({ userId, stories: result?.data?.stories }));
      } catch (err) {
        console.log(err);
        return;
      }
    };

    const handleMount = async () => {
      await handleRefresh();
      await fetchStories();
      setIsLoading(false);
    };

    handleMount();
  }, [isOpen, dispatch, axiosPrivate, userId, tokenExpired]);

  return { isLoading };
}

// 在组件中直接使用
const { isLoading } = useFetchUserStories(isOpen, userId, tokenExpired, dispatch, axiosPrivate);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:27:17