ReactJS通知组件删除失效及本地存储使用咨询
问题解答
一、用localStorage实现通知存储与删除的可行性
完全可行。localStorage是浏览器端的持久化存储方案,适合存储这类不需要跨设备同步、仅需在当前浏览器保留状态的通知数据。但要注意:
- 存储容量有限(约5MB)
- 清理浏览器缓存/数据会丢失存储内容
- 仅在当前浏览器有效,无法跨设备同步
二、删除后通知重新出现的原因
你的代码中存在核心逻辑冲突:
- 每秒更新的
currentDate或productList变化时,会触发重新生成所有通知并直接覆盖notifications状态 - 删除操作只是临时从
notifications数组中移除了项,但下一次状态更新(每秒一次)又会把所有符合条件的产品通知重新生成并塞回去,导致删除的通知“复活”
三、修复方案
要解决这个问题,需要记录用户已删除的通知ID,在生成新通知时自动过滤掉这些已删除的项,同时把已删除的ID存在localStorage中持久化。
关键改动点:
- 新增
deletedNotificationIds状态,存储已删除的通知ID列表,从localStorage初始化 - 生成通知时,过滤掉
deletedNotificationIds中的ID对应的项 - 删除逻辑改为记录ID而非直接过滤通知对象,同时更新localStorage
- 修正列表key的错误(之前用
notification对象作为key,应该用唯一的notification.id) - 优化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
相关产品推荐
相关产品推荐

