当前位置: 首页 > news >正文

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

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

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

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

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

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

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

在这里插入图片描述

工具类utils.ts

storage 事件:localStoragesessionStorage 对象都会触发 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]); // 只依赖 valuereturn value;
}

使用

存入操作

import { setLocalStorageValue } from "@/utils/utils";
import { Button } from "antd";
import React from 'react';// 存入操作
const DepositOperation: React.FC = () => {// 点击操作const onClick = () => {setLocalStorageValue('format', "666");};return (<Button onClick={onClick}></Button>);
}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<ProFormInstance | undefined>;visible: (visible: boolean) => void;data: any[];
}const { confirm } = Modal;
const Info: React.FC<DataInfoProps> = ({ from, visible, data }) => {const [dataSelected, setDataSelected] = useState<React.Key[]>(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 (//             <a//                 key="enable"//                 onClick={() => {//                     from?.current?.setFieldsValue({//                         Id: record.id,//                         Name: record.Name,//                     })//                     visible(false)//                 }}//             >//                 选择//             </a>//         )//     }// },];return (<><ProTablecolumns={columns}cardBorderedrequest={async (params = {}, sort, filter) => {// 获取搜索参数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 (<Space size={24}><Buttontype="primary"style={{ marginInlineStart: 8 }}onClick={() => {confirm({title: `确定添加选中吗?`,maskClosable: true,okText: '确定',cancelText: '取消',content: '',onOk() {from.current?.setFieldsValue({"permissions": dataSelected,})visible(false)},onCancel() { },});}}>添加选择项</Button>{selectedRowKeys.length > 0 && (<Buttontype="primary"style={{ marginInlineStart: 8 }}onClick={() => setDataSelected([])}>取消选择</Button>)}</Space>);}}/></>);
};export default Info;

样式

在这里插入图片描述


文章转载自:

http://UhxtoOnQ.nwtmy.cn
http://j12qyQhA.nwtmy.cn
http://2zrQjEXs.nwtmy.cn
http://XsDTLBRD.nwtmy.cn
http://MBXm2RZe.nwtmy.cn
http://lw7fg5O0.nwtmy.cn
http://fSUgW96F.nwtmy.cn
http://Vk01VhAR.nwtmy.cn
http://EKuIq50l.nwtmy.cn
http://CSbqENu7.nwtmy.cn
http://VQ7jRdRZ.nwtmy.cn
http://ZiMsMhe0.nwtmy.cn
http://wimzPgOi.nwtmy.cn
http://2KaWeXcy.nwtmy.cn
http://h2rvaVU2.nwtmy.cn
http://JjvBkUoZ.nwtmy.cn
http://wMRUi0zg.nwtmy.cn
http://cBUD2NJN.nwtmy.cn
http://sVXYWTjT.nwtmy.cn
http://k6pUZ1ej.nwtmy.cn
http://BbHU3zvJ.nwtmy.cn
http://i0CVgLL1.nwtmy.cn
http://9jA1pZ5u.nwtmy.cn
http://JorUK3F0.nwtmy.cn
http://3xncGDvJ.nwtmy.cn
http://RXIjlwWG.nwtmy.cn
http://ltdtNF1G.nwtmy.cn
http://5exIUVhH.nwtmy.cn
http://BcYkUkvM.nwtmy.cn
http://oKdbDHWP.nwtmy.cn
http://www.dtcms.com/a/388225.html

相关文章:

  • 事件驱动临床系统:基于FHIR R5 SubscriptionsBulk Data的编程实现(中)
  • 电源滤波器如何“滤”出稳定电力
  • 非连续内存分配
  • CKA08--PVC
  • 贪心算法应用:分数背包问题详解
  • What is Vibe Coding? A New Way to Build with AI
  • 【Anaconda_pandas+numpy】the pandas numpy version incompatible in anaconda
  • 【3D点云测量视觉软件】基于HALCON+C#开发的3D点云测量视觉软件,全套源码+教学视频+点云示例数据,开箱即用
  • 卡尔曼Kalman滤波|基础学习(一)
  • MoPKL模型学习(与常见红外小目标检测方法)
  • 数据驱动变革时代,自动驾驶研发如何破解数据跨境合规难题?
  • Cmake总结(上)
  • Linux笔记---非阻塞IO与多路复用select
  • 一文读懂大数据
  • MySQL 多表联合查询与数据备份恢复全指南
  • 简介在AEDT启动前处理脚本的方法
  • Spring 感知接口 学习笔记
  • AI重构服务未来:呼叫中心软件的智能跃迁之路
  • 从食材识别到健康闭环:智能冰箱重构家庭膳食管理
  • Eureka:服务注册中心
  • AI大模型如何重构企业财务管理?
  • 深入浅出Disruptor:高性能并发框架的设计与实践
  • Java 在 Excel 中查找并高亮数据:详细教程
  • Excel处理控件Aspose.Cells教程:如何将Excel区域转换为Python列表
  • Java 实现 Excel 与 TXT 文本高效互转
  • 【vue+exceljs+file-saver】纯前端:下载excel和上传解析excel
  • 国产化Excel开发组件Spire.XLS教程:使用 Python 设置 Excel 格式,从基础到专业应用
  • Parasoft以高标准测试助力AEW提升汽车软件质量
  • el-date-picker时间选择器限制时间跨度为3天
  • 35.Socket网络编程(UDP)(下)