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

将RTK Query结果存入useState触发无限循环报错

解决RTK Query数据存入useState导致的无限重渲染问题

你遇到的Maximum update depth exceeded警告,根源是RTK Query每次返回的notifications数组都是新的引用(即使内容完全不变),导致依赖该数组的useEffect反复执行setNotificationList,触发组件重新渲染,进而再次获取新引用的数组,形成无限循环。

下面提供两种解决方案,优先推荐方案一(符合RTK Query的设计理念):

方案一:使用RTK Query的updateQueryData直接修改缓存(推荐)

RTK Query内置了修改缓存的能力,无需额外维护本地state,既能保证和服务器数据的同步,又能处理本地操作(比如删除通知)。

修改步骤:

  1. 移除本地的notificationList state和对应的useEffect
  2. 从useGetUserNotificationsQuery中解构出updateQueryData方法
  3. 在删除逻辑中调用updateQueryData直接修改缓存

修改后的核心代码:

// 获取查询数据和缓存更新方法
const { data: notifications = [], isLoading, isError, updateQueryData } = useGetUserNotificationsQuery();

// 处理删除通知
const handleDeleteNotification = () => {
  // 使用Immer语法修改缓存的draft对象
  updateQueryData('getUserNotifications', undefined, (draft) => {
    const targetIndex = draft.findIndex(n => n._id === selectedNotification._id);
    if (targetIndex !== -1) {
      draft.splice(targetIndex, 1);
    }
  });
  handleMenuClose();
};

完整组件修改示例(关键部分):

const NotificationList = () => {
  const [anchorEl, setAnchorEl] = useState(null);
  const [selectedNotification, setSelectedNotification] = useState(null);

  // 解构出updateQueryData方法
  const { data: notifications = [], isLoading, isError, updateQueryData } = useGetUserNotificationsQuery();
  const [userNotificationRead] = useUserNotificationReadMutation();

  // ...其他方法(handleMenuOpen、handleMenuClose等)保持不变

  const handleDeleteNotification = () => {
    // 直接更新RTK Query缓存
    updateQueryData('getUserNotifications', undefined, (draft) => {
      const index = draft.findIndex(
        notification => notification._id === selectedNotification._id
      );
      if (index !== -1) {
        draft.splice(index, 1);
      }
    });
    handleMenuClose();
  };

  // ...加载状态判断保持不变

  // 空状态判断改用notifications
  if(!notifications.length) {
    // ...原空状态代码,保持不变
  }

  // 渲染时直接使用notifications数组
  return (
    <div>
      {notifications.map((notification) => (
        // ...原渲染逻辑,保持不变
      ))}
      {/* ...菜单代码,保持不变 */}
    </div>
  );
};

方案二:保留本地state,避免不必要更新

如果必须维护本地state,需要在useEffect中判断数组内容是否真的变化,再执行更新:

方法1:内容比较后更新

useEffect(() => {
  // 简单比较数组内容(适合结构简单的对象),不同时才更新
  const contentChanged = JSON.stringify(notifications) !== JSON.stringify(notificationList);
  if (contentChanged) {
    setNotificationList(notifications);
  }
}, [notifications, notificationList]);

注意:如果通知对象包含日期、函数等复杂类型,JSON.stringify会失效,此时可以使用深比较工具(如lodash的isEqual)。

方法2:用useMemo缓存稳定的数组引用

// 基于内容生成稳定的引用
const stableNotifications = useMemo(() => notifications, [JSON.stringify(notifications)]);

useEffect(() => {
  setNotificationList(stableNotifications);
}, [stableNotifications]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:58:17