React+antd搭建监听localStorage变化多页面更新+纯js单页面table模糊、精确查询、添加、展示功能

news/2025/9/20 19:26:43/文章来源:https://www.cnblogs.com/tlnshuju/p/19102757

React+antd搭建监听localStorage变化多页面更新+纯js单页面table模糊、精确查询、添加、展示功能

2025-09-20 19:22  tlnshuju  阅读(0)  评论(0)    收藏  举报

React+antd实现监听localStorage变化多页面更新

在开发中遇见一个问题,就是一个后台页面,需要通过改变业态来影响table的查询内容,如下图

这个需要改变本地缓存的localStorage,再通知其他组件进行对应操作

由于我是后端开发,对react说熟也熟,说不熟也不熟

而页面之间的通信我只会 pubsub-js 和 props,props是父与子的互传,pubsub-js 又需要引入,本来打包就够慢了,不想引入新的包,而且localStorage后续还得监听多个值,用 pubsub 就颇为麻烦了

还有个方法就是轮训去查localStorage的值如果改变就执行对应操作,但如果一个页面多组件需要获取到localStorage新值的情况下有点浪费性能了

在这里插入图片描述

工具类utils.ts

storage 事件:localStoragesessionStorage 对象都会触发 storage 事件,当其他窗口或标签页对存储进行更改时会触发该事件。您可以通过添加 storage 事件监听器来捕获变化,并在回调函数中执行相应的操作。

删除监听器(移除事件监听)原因:

// 设置localStorage值的辅助函数
export function setLocalStorageValue(key: string, value: string) {
try {
window.localStorage.setItem(key, value);
// 触发自定义事件,通知同标签页的监听器
window.dispatchEvent(new CustomEvent('localStorageChange', {
// 事件携带的数据
detail: { key, value
}
}));
} catch (error) {
console.error(`Error setting localStorage key "${key
}":`, error);
}
}
// 自定义Hook:用于监听localStorage特定键的变化
export function useLocalStorage(key: any, callback: (newValue: string | undefined) =>
void) {
const [value, setValue] = useState(() =>
{
try {
// 直接从localStorage获取当前值
const item = window.localStorage.getItem(key);
return item || undefined;
} catch (error) {
console.error(`Error reading localStorage key "${key
}":`, error);
return undefined;
}
});
// 监听storage变化
useEffect(() =>
{
const handleStorageChange = (e: any) =>
{
if (e.key === key) {
setValue(e.newValue);
}
};
// 监听storage事件(跨标签页)
window.addEventListener('storage', handleStorageChange);
// 监听自定义事件(同标签页)
const handleCustomEvent = (e: any) =>
{
if (e.detail.key === key) {
setValue(e.detail.value);
}
};
// 监听自定义事件
window.addEventListener('localStorageChange', handleCustomEvent);
// 删除监听器是 React useEffect Hook 的标准清理模式,确保应用程序的性能和稳定性
return () =>
{
window.removeEventListener('storage', handleStorageChange);
window.removeEventListener('localStorageChange', handleCustomEvent);
};
}, [key]);
// 使用 useRef 保存最新的回调
const callbackRef = useRef(callback);
callbackRef.current = callback;
// 使用 ref 来调用回调,避免重复触发
useEffect(() =>
{
if (value !== undefined) {
callbackRef.current(value);
}
}, [value]);
// 只依赖 value
return value;
}

使用

存入操作

import { setLocalStorageValue } from "@/utils/utils";
import { Button } from "antd";
import React from 'react';
// 存入操作
const DepositOperation: React.FC = () => {
// 点击操作
const onClick = () => {
setLocalStorageValue('format', "666");
};
return (
);
}
export default DepositOperation;

监听操作

import { useLocalStorage } from "@/utils/utils";
import React from 'react';
// 监听操作
const ListeningOperation: React.FC = () => {
// ....其他无关代码
// 业态监听,修改时做相应动作
useLocalStorage('format', (value) => {
console.log('format', value)
});
return (
<>
);
}
export default ListeningOperation;

测试

为啥不是 666,是因为我没有存 666,存的是一个对象,上面的代码只是示例,下面的图片只是验证方法可行可用

在这里插入图片描述

纯js单页面table模糊、精确查询、添加、展示功能

下面只是记录一个纯js实现的页面,通过list生产列表,js精确、模糊查询,点击行选中

代码

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable array-callback-return */
import type { ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { Button, Modal, Space, Table } from 'antd';
import type { ProFormInstance } from '@ant-design/pro-form';
import React, { useState } from 'react';
type DataInfoProps = {
from: React.RefObject;
visible: (visible: boolean) => void;
data: any[];
}
const { confirm } = Modal;
const Info: React.FC = ({ from, visible, data }) => {
const [dataSelected, setDataSelected] = useState(from.current?.getFieldValue("permissions") as React.Key[] || [])
const columns: ProColumns[] = [
{
title: 'ID',
dataIndex: 'value',
hideInTable: true,
},
{
title: '名称',
dataIndex: 'label',
hideInTable: true,
},
{
title: '状态',
dataIndex: 'status',
hideInTable: true,
valueEnum: {
"0": {
text: '未选择',
},
"1": {
text: '已选择',
}
},
},
// {
//     title: "序号",
//     dataIndex: 'index',
//     valueType: 'index',
//     width: 80,
//     align: 'center',
// },
{
title: 'ID',
dataIndex: 'value',
align: 'center',
search: false,
},
{
title: '名称',
dataIndex: 'label',
align: 'center',
search: false,
},
// {
//     title: '操作',
//     valueType: 'option',
//     key: 'option',
//     width: 120,
//     render: (t, record) => {
//         return (
//              {
//                     from?.current?.setFieldsValue({
//                         Id: record.id,
//                         Name: record.Name,
//                     })
//                     visible(false)
//                 }}
//             >
//                 选择
//
//         )
//     }
// },
];
return (
<>{
// 获取搜索参数
const Id = params.value || ''; // ID - 精确匹配
const Name = params.label || ''; // 名称 - 模糊查询
const { status } = params; // 状态查询
// 过滤数据
const filteredData = data.filter(item => {
// ID精确匹配
const idMatch = !Id || (item.value && item.value.toString() === Id);
// 名称模糊查询
const nameMatch = !Name ||
(item.label && item.label.toString().toLowerCase().includes(Name.toLowerCase()));
// 状态查询:根据dataSelected判断是否已选择
let statusMatch = true;
if (status !== undefined && status !== null && status !== '') {
const isSelected = dataSelected.includes(item.value);
if (status === "1") {
statusMatch = isSelected; // 已选择
} else if (status === "0") {
statusMatch = !isSelected; // 未选择
}
}
return idMatch && nameMatch && statusMatch;
});
// 应用排序
const sortedData = [...filteredData];
if (sort && Object.keys(sort).length > 0) {
const sortField = Object.keys(sort)[0];
const sortOrder = sort[sortField];
sortedData.sort((a, b) => {
const aValue = a[sortField] || '';
const bValue = b[sortField] || '';
if (sortOrder === 'ascend') {
return aValue.toString().localeCompare(bValue.toString());
}
return bValue.toString().localeCompare(aValue.toString());
});
}
return {
data: sortedData,
success: true,
total: filteredData.length,
};
}}
options={false}
rowKey="value"
pagination={{
defaultPageSize: 10,
pageSizeOptions: [10, 20, 30, 40, 50, 100, 200, 500, 1000],
showSizeChanger: true,
showQuickJumper: true,
}}
search={{
labelWidth: 'auto',
defaultCollapsed: false,
showHiddenNum: true,
}}
scroll={{ x: 'max-content' }}
rowSelection={{
selectedRowKeys: dataSelected,
// selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT],
// onChange: (newSelectedRowKeys: React.Key[], selectedRows: any[], info: { type: string }) => {
//     console.log('info', info)
//     console.log('type', info.type)
//     // 普通选择操作:直接设置新的选择
//     setDataSelected(newSelectedRowKeys);
// },
onSelect: (record: any, selected: boolean, selectedRows: any, changeRows: any) => {
const newSelectedRowKeys = [...dataSelected];
const index = newSelectedRowKeys.indexOf(record.value);
if (index > -1) {
// 如果已经选中,则取消选择
newSelectedRowKeys.splice(index, 1);
} else {
// 如果未选中,则添加选择
newSelectedRowKeys.push(record.value);
}
setDataSelected(newSelectedRowKeys);
},
onSelectAll: (selectedRowKeys: any, selectedRows: any, changeRows: any) => {
const newSelectedRowKeys = [...dataSelected];
changeRows.forEach((item: any) => {
const index = newSelectedRowKeys.indexOf(item.value);
if (index > -1) {
// 如果已经选中,则取消选择
newSelectedRowKeys.splice(index, 1);
} else {
// 如果未选中,则添加选择
newSelectedRowKeys.push(item.value);
}
})
setDataSelected(newSelectedRowKeys);
}
}}
dateFormatter="string"
headerTitle=""
onRow={(record) => ({
onClick: (event) => {
// 阻止事件冒泡,避免与复选框点击冲突
if ((event.target as HTMLElement).closest('.ant-checkbox-wrapper')) {
return;
}
const key = record.value;
const newSelectedRowKeys = [...dataSelected];
const index = newSelectedRowKeys.indexOf(key);
if (index > -1) {
// 如果已经选中,则取消选择
newSelectedRowKeys.splice(index, 1);
} else {
// 如果未选中,则添加选择
newSelectedRowKeys.push(key);
}
setDataSelected(newSelectedRowKeys);
},
style: { cursor: 'pointer' }
})}
tableAlertOptionRender={({ selectedRowKeys, selectedRows, onCleanSelected }) => {
return ({
confirm({
title: `确定添加选中吗?`,
maskClosable: true,
okText: '确定',
cancelText: '取消',
content: '',
onOk() {
from.current?.setFieldsValue({
"permissions": dataSelected,
})
visible(false)
},
onCancel() { },
});
}}
>
添加选择项
{selectedRowKeys.length > 0 && (setDataSelected([])}
>
取消选择
)}
);
}}
/>
);
};
export default Info;

样式

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/908517.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

暗黑破坏神4 任务-坚守传统-向古老的雕像展示你坚守的传统

暗黑破坏神4 任务-坚守传统-向古老的雕像展示你坚守的传统 发一个“确定”的表情即可。

202509_NBWS_logbool

流量分析,正则匹配,布尔盲注,pyshark,Tags:流量分析,正则匹配,布尔盲注,pyshark,DASCTF 0x00. 题目 找到flag,格式为DASCTF{} 附件路径:https://pan.baidu.com/s/1GyH7kitkMYywGC9YJeQLJA?pwd=Zmxh#list/path=/CTF附…

Kubernetes权威指南-深入理解Pod Service

Pod是Kubernetes最小调度单元,将多个紧密协作的容器组合为一个逻辑主机,共享网络、存储与IP。通过YAML定义容器、卷、健康检查等配置,支持静态Pod、Init容器、ConfigMap等高级特性,并借助Service实现稳定的服务发现…

详细介绍:jeecg-boot3.7.0对接钉钉登录(OAuth2.0)

详细介绍:jeecg-boot3.7.0对接钉钉登录(OAuth2.0)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

C++编程软件 Dev-C++ 安装及使用流程

目录第 1 步:下载 Dev-C++第 2 步:安装 Dev-C++第 3 步:第一次打开 Dev-C++ 时的配置第4步:编写一个 C++ 程序 第 1 步:下载 Dev-C++ 首先,我们需要找一个地方下载 Dev-C++ 软件。 可以直接点击 这个链接 下载。…

DLL植入漏洞分类与微软安全响应指南

本文详细解析微软对DLL植入漏洞的分类方法,涵盖应用程序目录、当前工作目录和PATH目录三种场景的威胁评估标准,并说明微软针对不同场景的安全响应策略与修复优先级。DLL植入漏洞的分类 | MSRC博客 本文章翻译自Secur…

4980:拯救行动

题目 总时间限制: 1000ms 内存限制: 65536kB 描述 公主被恶人抓走,被关押在牢房的某个地方。牢房用N*M (N, M <= 200)的矩阵来表示。矩阵中的每项可以代表道路(@)、墙壁(#)、和守卫(x)。 英勇的骑士(r)决定…

java03-wxj

好的,我们来逐一详细解答这些问题。1. 什么样的方法应该用static修饰?不用static修饰的方法往往具有什么特性? 应该使用 static 修饰的方法:工具方法(Utility Methods): 执行一个与任何特定对象实例无关的通用任…

题解:P13969 [VKOSHP 2024] Exchange and Deletion

题面: 我们考虑从图论意义计数,把 swap 改成连边,由于交换完前面的点直接被删了,所以只保留从后向前的连边。 那么最后连到 \(n-k\) 前的点的数值等于链头,而 \(n-k\) 后的点和链上非链头的点实际上都被删了。手玩…

基于MATLAB的车牌识别系统 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

市场交易反心理特征之二:忽视热点切换的苗头

案例:2017年8月18日,万科A。2017年8月18日,万科A 万科A小波段延续万科A一个波段完成​情况描述:从第一次触及无穷成本线止跌后大资金进入开始,连续2-3次出现买点,都能选出。但是都直接鼠标滑过,没有过脑子,显然…

Linux服务器上安装配置GitLab的步骤

在Linux服务器上安装GitLab是一个涉及多个步骤的过程。以下是详细的步骤,遵从GitLab官方推荐的做法以确保系统的稳定性和性能。 服务器要求和前提条件:一台运行支持的Linux操作系统的服务器,建议使用CentOS 7。 至少…

贪心算法应用:投资组合再平衡问题详解 - 实践

贪心算法应用:投资组合再平衡问题详解 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…

MCP:Trae中集成Playwright 实现网页自动化测试

Trae IDE 可以通过智能问答的形式补齐代码,纠正程序中的错误,根据用户的自然语言,实现AI自动编程。近期使用了一下Trae,发现很强大。我把一个有前后端的项目导入Trae IDE,当时还有一些报错,但是很快在Trae 的提示…

C语言中的字符、字符串及内存操作函数详细讲解

在C语言中,字符和字符串的处理是基本且重要的概念。字符在C中通常由 char类型表示,而字符串则是以 null终止的字符数组。内存操作函数则提供了基本的内存处理能力,如复制、设置、比较等内存块。 字符操作 字符使用 …

06、訊息收集

1、使用nmap探测magedu.com开放的端口号和服务指纹 2、使用指纹识别工具探测magedu.com采用的建站模板 3、搜集magedu.com的子域名有哪些

AI 智能体与 Coze 工作流实践:小红书对标账号采集 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

在Linux中设定账户密码的安全性策略

在Linux环境下,确保账户密码的安全性是保护系统安全的重要环节。要设置强健的密码策略,需要从多个层面制定规则,这些包括密码复杂性、长度、有效期限、历史记录限制和尝试次数限制等方面。 密码复杂性设定和管理:密…

对比六种JavaScript全文搜索库 fuse.js 、 lunr 、 flexsearch 、 minisearch 、 search-index 、 js-sea

以下是针对六种JavaScript全文搜索库的对比分析,综合功能特性、性能表现及适用场景: 核心特性对比‌Fuse.js‌‌算法‌:基于Bitap算法的模糊搜索,支持拼写容错和多种匹配模式(前缀、后缀、逻辑组合等)7。 ‌配置…

精选 4 款基于 .NET 开源、功能强大的 Windows 系统优化工具,助力轻松提升 Windows 系统性能与使用体验!

前言 今天大姚给大家推荐 4 款基于 .NET 开源、功能强大的 Windows 系统优化工具,希望可以帮助你轻松提升 Windows 系统性能与使用体验。 Optimizer Optimizer 是一款基于 .NET 开源(GPL-3.0 license)、功能强大的 …