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

React触发超时函数时使用旧状态数据的问题排查

React通知组件自动关闭时状态过期问题的解决办法

你开发的React通知提供者中,手动关闭通知功能正常,但自动超时关闭时,父组件的finished函数会用到旧的notifications状态,导致无法正确移除对应通知。这是React闭包特性导致的——超时函数捕获了组件初始渲染时的状态快照,而非最新状态。不需要重新设计功能,只需要调整状态更新方式和依赖项就能解决。

问题根源

  1. 父组件的finished、info等方法直接依赖当前notifications状态,当超时触发这些方法时,闭包保留的是函数创建时的旧状态数组。
  2. 子组件NotificationCard的useEffect依赖项为空数组,仅在组件挂载时创建超时,捕获的是初始的close函数和notification值,无法响应后续状态变化。

解决方案

1. 父组件改用函数式更新状态

将所有修改notifications的方法改为函数式更新,确保每次都基于最新状态计算新数组:

export const NotificationProvider = ({ children }: { children?: JSX.Element }) => {
  const [notifications, setNotifications] = useState<Notification[]>([]);

  // 省略其他不变代码...

  const info = (notification: Notification) => {
    setNotifications(prev => [...prev, createNotification({ ...notification, level: NotificationLevel.INFO })]);
  };

  const success = (notification: Notification) => {
    setNotifications(prev => [...prev, createNotification({ ...notification, level: NotificationLevel.SUCCESS })]);
  };

  const warning = (notification: Notification) => {
    setNotifications(prev => [...prev, createNotification({ ...notification, level: NotificationLevel.WARNING })]);
  };

  const error = (notification: Notification) => {
    setNotifications(prev => [...prev, createNotification({ ...notification, level: NotificationLevel.ERROR })]);
  };

  const finished = (notification: Notification) => {
    setNotifications(prev => prev.filter((n) => n.id !== notification.id));
  };

  // 省略return部分...
};

2. 修复子组件的依赖项与函数缓存

调整子组件的useEffect依赖项,并用useCallback缓存close函数,避免不必要的重渲染和闭包问题:

import styles from "@styles/Notification.module.scss";
import { Notification } from "@utils/notificationProvider";
import { useEffect, useState, useCallback } from "react";

const NotificationCard = ({
  notification,
  onFinished
}: {
  notification: Notification;
  onFinished?: (notification: Notification) => void;
}) => {
  const [isOpen, setIsOpen] = useState(true);

  const close = useCallback(() => {
    setIsOpen(false);

    setTimeout(() => {
      if (onFinished) {
        onFinished(notification);
      }
    }, 500);
  }, [notification, onFinished]);

  useEffect(() => {
    const timeout = setTimeout(() => {
      close();
    }, notification.timeout);

    return () => {
      clearTimeout(timeout);
    };
  }, [notification.timeout, close]);

  return (
    <div onClick={() => close()} className={isOpen ? styles.notificationCard : styles.notificationCardClosed}>
      {notification.message}
    </div>
  );
};

export default NotificationCard;

说明

  • 函数式更新:setNotifications(prev => ...) 让React始终基于最新的状态快照计算新数组,彻底规避闭包导致的旧状态问题。
  • useCallback:缓存close函数,仅当notification或onFinished变化时才重新创建,避免useEffect频繁触发。
  • 完善依赖项:确保useEffect能响应notification.timeout的变化,同时保证超时逻辑使用最新的close函数。

内容的提问来源于stack exchange,提问作者Andreas Halvorsen Tollånes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:20:29