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 事件:
localStorage
和sessionStorage
对象都会触发storage
事件,当其他窗口或标签页对存储进行更改时会触发该事件。您可以通过添加storage
事件监听器来捕获变化,并在回调函数中执行相应的操作。删除监听器(移除事件监听)原因:
防止内存泄漏
如果不移除监听器,每次组件重新渲染时都会添加新的监听器
旧的监听器会继续存在于内存中,无法被垃圾回收
随着时间的推移,会导致内存使用量不断增加
避免重复执行
多个相同的监听器会同时响应同一个事件
导致回调函数被多次执行,可能引发意外的副作用
特别是在
setValue
被多次调用时,会造成组件状态混乱组件卸载时的清理
当使用该 Hook 的组件被卸载时,需要清理所有与之相关的事件监听
确保不会尝试更新已卸载组件的状态(避免 React 警告)
// 设置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,一经查实,立即删除!