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

ReactJS通知组件删除失效及本地存储使用咨询

问题解答

一、用localStorage实现通知存储与删除的可行性

完全可行。localStorage是浏览器端的持久化存储方案,适合存储这类不需要跨设备同步、仅需在当前浏览器保留状态的通知数据。但要注意:

  • 存储容量有限(约5MB)
  • 清理浏览器缓存/数据会丢失存储内容
  • 仅在当前浏览器有效,无法跨设备同步

二、删除后通知重新出现的原因

你的代码中存在核心逻辑冲突:

  • 每秒更新的currentDate或productList变化时,会触发重新生成所有通知并直接覆盖notifications状态
  • 删除操作只是临时从notifications数组中移除了项,但下一次状态更新(每秒一次)又会把所有符合条件的产品通知重新生成并塞回去,导致删除的通知“复活”

三、修复方案

要解决这个问题,需要记录用户已删除的通知ID,在生成新通知时自动过滤掉这些已删除的项,同时把已删除的ID存在localStorage中持久化。

关键改动点:

  1. 新增deletedNotificationIds状态,存储已删除的通知ID列表,从localStorage初始化
  2. 生成通知时,过滤掉deletedNotificationIds中的ID对应的项
  3. 删除逻辑改为记录ID而非直接过滤通知对象,同时更新localStorage
  4. 修正列表key的错误(之前用notification对象作为key,应该用唯一的notification.id)
  5. 优化localStorage的更新时机,避免不必要的重复存储

修改后的完整代码

import React, { useEffect, useState } from "react";
import { IoMdNotificationsOutline } from "react-icons/io";
import "./notif.css";
import { getDocs, collection } from "firebase/firestore";
import { database } from "../config/firebase";
import { MdCancel } from "react-icons/md";

const NotificationComponent = () => {
    const [productList, setProductList] = useState([]);
    const productCollectionRef = collection(database, "singleProduct");

    async function getProductList() {
        try {
            const data = await getDocs(productCollectionRef);
            const filteredData = data.docs.map((doc) => ({
                ...doc.data(),
                id: doc.id,
            }));
            setProductList(filteredData.reverse());
        } catch (err) {
            console.error(err);
        }
    }

    useEffect(() => {
        getProductList();
    }, []);

    const [showNotifications, setShowNotifications] = useState(false);
    // 从localStorage读取已删除的通知ID列表
    const [deletedNotificationIds, setDeletedNotificationIds] = useState(() => {
        const stored = localStorage.getItem("deletedNotificationIds");
        return stored ? JSON.parse(stored) : [];
    });
    const [notifications, setNotifications] = useState([]);
    const [currentDate, setCurrentDate] = useState(new Date());
    const [counter, setCounter] = useState(0);

    const toggleNotifications = () => {
        setShowNotifications(!showNotifications);
        if (showNotifications) {
            setCounter(0);
        }
    };

    function isWithinOneWeek(date) {
        const targetDate = new Date(date);
        const checkWeekAhead = new Date();
        checkWeekAhead.setDate(checkWeekAhead.getDate() + 7);
        return Math.abs(targetDate.getTime() - checkWeekAhead.getTime()) <= 7 * 24 * 60 * 60 * 1000;
    }

    useEffect(() => {
        const timer = setInterval(() => {
            setCurrentDate(new Date());
        }, 1000);
        return () => clearInterval(timer);
    }, []);

    // 生成通知时过滤已删除的ID
    useEffect(() => {
        const newNotifications = [];
        productList.forEach((product) => {
            // 如果这个产品的通知已经被删除,跳过
            if (deletedNotificationIds.includes(product.id)) return;

            const expirationDate = new Date(product.expirationDate.seconds * 1000);
            if (isWithinOneWeek(expirationDate)) {
                newNotifications.push({
                    id: product.id,
                    message: (
                        <div className="notificationMessage">
                            <p>The Product: {product.productName} will be expired in one week</p>
                            <p>Product Identifier: {product.uniqueIdentifier}{product.serialNum}</p>
                            <p>{expirationDate.toLocaleDateString()}</p>
                        </div>
                    ),
                });
            } else if (currentDate >= expirationDate) {
                newNotifications.push({
                    id: product.id,
                    message: (
                        <div className="notificationMessage">
                            <p>The Product: {product.productName} is expired.</p>
                            <p>Product Identifier: {product.uniqueIdentifier}{product.serialNum}</p>
                            <p>{expirationDate.toLocaleDateString()}</p>
                        </div>
                    ),
                });
            }
        });
        setNotifications(newNotifications);
        setCounter(newNotifications.length);
    }, [productList, currentDate, deletedNotificationIds]);

    // 持久化已删除的ID到localStorage
    useEffect(() => {
        localStorage.setItem("deletedNotificationIds", JSON.stringify(deletedNotificationIds));
    }, [deletedNotificationIds]);

    // 修改删除逻辑:记录已删除的ID
    const handleDelete = (notificationId) => {
        setDeletedNotificationIds(prev => [...prev, notificationId]);
    };

    return (
        <div className="notification-component">
            <div>
                <h1>{currentDate.toLocaleDateString()}</h1>
            </div>
            <div className="notification-icon" onClick={toggleNotifications}>
                <IoMdNotificationsOutline className="icon" />
                {!showNotifications && counter > 0 && (
                    <span className="notification-counter">{counter}</span>
                )}
            </div>
            {showNotifications && (
                <div className="notification-list">
                    <h3>Notifications</h3>
                    <ul>
                        {notifications.map((notification) => (
                            <li key={notification.id}> {/* 修正为唯一ID作为key */}
                                {notification.message}
                                <span
                                    className="deleteNotif"
                                    onClick={() => handleDelete(notification.id)}
                                >
                                    <MdCancel />
                                </span>
                            </li>
                        ))}
                    </ul>
                </div>
            )}
        </div>
    );
};

export default NotificationComponent;

补充说明

  • 现在删除通知时,会把对应的产品ID加入deletedNotificationIds,后续生成通知时会自动跳过这些产品,不会再出现
  • deletedNotificationIds会存在localStorage中,刷新浏览器后仍能记住已删除的通知
  • 移除了原代码中不必要的notifications存储逻辑,因为通知是动态生成的,只需持久化已删除的ID即可

内容的提问来源于stack exchange,提问作者Piolo Pascual Besinga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:03:20