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

React自定义Toast中setState配合setTimeout导致已删元素复现问题

问题根因

代码触发了React状态操作的典型闭包陷阱:

  • removeToast 方法中直接引用了当前渲染上下文的toasts状态,而createToast中注册的setTimeout回调会保留创建回调时的上下文快照,5秒后执行时拿到的toasts不是最新的状态值,而是创建定时器那一刻的旧状态。
  • 单次创建toast时旧状态和最新状态一致,所以运行正常;多次并发创建toast时,每个定时器回调拿到的都是创建对应toast时的旧状态,删除操作就会出现状态覆盖、误删其他toast的异常。
  • 手动删除功能正常是因为手动触发删除时,调用的是最新渲染上下文的removeToast,拿到的toasts是最新状态,不会出现闭包问题。

修复方案

只需要把removeToast中的状态更新改为函数式更新即可,函数式更新的入参永远是当前最新的状态值,不受闭包影响:

const useToastsContainer = createContainer(() => {
    const [toasts, setToasts] = useState<ToastItem[]>([]);

    useEffect(() => {
        console.log(toasts);
    }, [toasts]);

    const removeToast = (id: string) => {
        // 改为函数式更新,用prevToasts获取最新状态
        setToasts(prevToasts => prevToasts.filter((item) => item.id !== id));
    };

    const createToast = (type: ToastType, content: string) => {
        const id = v4();
        const toast = {
            id,
            type,
            content,
        };
        // 可选优化:这里也改为函数式更新,避免极端并发场景的状态覆盖问题
        setToasts(prevToasts => [...prevToasts, toast]);
        setTimeout(() => {
            removeToast(id);
        }, 5000);
    };

    return {
        toasts,
        removeToast,
        createToast,
    };
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:36:06