react-router-dom v6版本实现Tabs路由缓存切换
目录
文章目录
概要
效果
完整代码
概要
摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和react-router的机制就容易了.
想做到切换标签保留页面的内容不变,就要了解react的机制
首先虚拟DOM的机制就是对比,如果找不到就会重新挂载,找到了就更新。
React-Router的机制就是匹配路径,找到了就返回对应的路由组件,找不到返回为null
思路就是v6版本提供了Outlet这个输出子路元素的组件
一般我们会这样写:
但如果要实现标签的话,就不能这样写。但是切换路由,地址一变它就替换了原来的了。所以要做的就是保留所有打开的标签的子路由元素:
主要目的就是保留所有的元素,隐藏路由就行,这样react diff时,还是会找到对应key的路由,这样它只是会更新路由的组件,而不会重新挂载。
如这样写:
效果
完整代码
新建一个html复制进去就可以运行了
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>React-router-dom tabs</title><script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.23.2/babel.min.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/antd@5.10.1/dist/reset.min.css"><script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.10/dayjs.min.js"></script><script src="https://cdn.jsdelivr.net/npm/antd@5.10.1/dist/antd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@ant-design/pro-components@2.6.30/dist/pro-components.min.js"></script><script src="https://cdn.jsdelivr.net/npm/@remix-run/router@1.10.0/dist/router.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/react-router@6.17.0/dist/umd/react-router.production.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/react-router-dom@6.17.0/dist/umd/react-router-dom.production.min.js"></script>
</head>
<body><div id="app"></div><script type="text/babel" data-preset="env,react">const { useCallback, useMemo, useEffect, useRef, useState } = Reactconst { ProLayout } = ProComponentsconst { createHashRouter, useOutlet, Navigate, RouterProvider, useLocation, Link, useNavigate } = ReactRouterDOMconst { Tabs, Button, Space, Row, Col, Input, } = antdconst Home = () => {return <div>Home <Input></Input></div>}const DemoA = () => {return <div>DemoA <Input></Input></div>}const DemoB = () => {return <div>DemoB <Input></Input></div>}const ViewPage = (props) => {const outlet = useOutlet()return props.render(outlet)}const BasicLayout = () => {const nav = useNavigate()const location = useLocation()const route = routes[0]const [menuDataMap] = useState(() => new Map())const cacheOutletElements = useRef({}).currentconst [tabActiveKey, setTabActiveKey] = useState('')const [tabItems, setTabItems] = React.useState([])const addTab = useCallback((item) => {const existItem = tabItems.find(it => it.key === item.key)if (!existItem) {setTabItems([...tabItems, {key: item.key,path: item.path,label: item.name}])}setTabActiveKey(item.key)}, [tabItems])const handleSelectMenu = useCallback((selectedKeys) => {console.log('handleSelectMenu', selectedKeys)let menuKey = selectedKeys[selectedKeys.length - 1]let item = menuDataMap.get(menuKey)if (item && item.path) {addTab(item)}}, [addTab])const handleTabChange = useCallback((activeKey) => {setTabActiveKey(activeKey)const item = tabItems.find(d => d.key === activeKey)nav(item.path)}, [tabItems, nav])const handleTabEditChange = useCallback((activeKey, action) => {if (action === 'remove') {delete cacheOutletElements[activeKey]const newItems = tabItems.filter(d => d.key !== activeKey)setTabItems(newItems)if (newItems.length) {handleTabChange(newItems[0].key)}}}, [tabItems, handleTabChange])const renderView = useCallback((routeElement) => {if (!cacheOutletElements[tabActiveKey]) {cacheOutletElements[tabActiveKey] = <div>{routeElement}</div>}return Object.keys(cacheOutletElements).map(key => {const element = cacheOutletElements[key]if (key === tabActiveKey) {return React.cloneElement(element, {key: key,style: {display: 'block'}})} else {return React.cloneElement(element, {key: key,style: {display: 'none'}})}})}, [cacheOutletElements, tabActiveKey])return <ProLayout route={route} onSelect={handleSelectMenu} location={location} menuItemRender={(item, defaultDom, menuProps) => {if (item.children) {return defaultDom}menuDataMap.set(item.path, item)return <Link to={item.path}>{defaultDom}</Link>}}><Tabs hideAdd type='editable-card' onEdit={handleTabEditChange} activeKey={tabActiveKey} onChange={handleTabChange} items={tabItems}></Tabs><ViewPage render={renderView}></ViewPage></ProLayout>}const routes = [{path: '/',element: <BasicLayout></BasicLayout>,children: [{index: true,element: <Navigate to="/home"></Navigate>}, {path: 'home',name: "Home",element: <Home></Home>}, {path: 'a',name: "DemoA",element: <DemoA></DemoA>}, {path: 'b',name: "DemoB",element: <DemoB></DemoB>}]}]const router = createHashRouter(routes, {})const App = () => {return <RouterProvider router={router}></RouterProvider>}ReactDOM.createRoot(document.getElementById('app')).render(<App></App>)</script>
</body>
</html>
相关文章:

react-router-dom v6版本实现Tabs路由缓存切换
目录 文章目录 概要 效果 完整代码 概要 摆了半年摊,好久没写代码了,今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大,但了解react的机制和rea…...

ArcGIS笔记9_数据源缺少空间参考信息?如何定义坐标系?
本文目录 前言Step 1 确定好要赋予给目标文件的恰当坐标系Step 2 定义坐标系 前言 有时从其他软件转换得到了shp文件,拖到ArcGIS后却出现“未知的空间参考:添加的数据源缺少空间参考信息 不能投影”的提示,如下图所示: 这种情况就…...

Elasticsearch实践:ELK+Kafka+Beats对日志收集平台的实现
可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个全文搜索引擎,它还提供了分布式的多用户能力,实时的分析,以及对复杂搜索语句的处理能力,使其在众多场景下,如企业搜索,日志和事件数据分析等…...

离线语音与IoT结合:智能家居发展新增长点
离线语音控制和物联网(IoT)相结合在家居中具有广泛的应用和许多优势。离线语音控制是指在设备在本地进行语音识别和处理,而不需要依赖云服务器进行处理。IoT是指借助网络,通过手机APP、小程序远程控制家居设备。 启英泰伦基于AI语…...

STM32MP135和STM32MP157的区别
本文介绍了STMicroelectronics公司推出的两款多核处理器STM32MP135和STM32MP157之间的区别,包括主频、集成硬件模块数量、内存大小和电压调节模块等方面。 STMicroelectronics是一家领先的半导体解决方案提供商,在嵌入式系统领域有着丰富的经验。他们…...

微信小程序文本横向无缝滚动
背景: 微信小程序中列表宽度不够长,其中某字段显示不完整,因此要使其自动滚动。 (最初看网上很多用定时器实现,但他们的案例中都只是一个横幅、用定时器也无所谓。但是我的需求中是一个上下无限滚动的列表,…...

Layui 主窗口调用 iframe 弹出框模块,获取控件的相应值
var iframeWindow window[layui-layer-iframe index]; iframeWindow.layui.tree............(这里就可以操作tree里面的内容了)。var chrild layero.find(iframe).contents(); chrild.layui.tree (这样是调用不到的)。var child layer.getChildFrame(); child.layui.tree(这…...

镜头边缘的解析力通常比中心差很多的原因是什么?
1、问题背景 之前有总结过一篇文章,“ 相机出图画面一半清晰,一半模糊的原因是什么?”里面有描述到关于镜头边缘的清晰度通常比中心要差的原因主要是光的折射导致的,有读者指出问题,折射率是和传输介质相关࿰…...

“控制情绪,理性交流”刍议
今天,本“人民体验官”还是回避推广人民日报官方微博文化产品《走出低谷期的9个习惯》。 截图:来源“人民体验官”推广平台 之前,由于笔者读过《人民日报》曾经发表过的关于“学会管理情绪 ”的文章,对文章中这些观点深表认同&…...
UI基础之插画分类优漫动游
一、UI插画分类 UI基础之插画分类 1.扁平插画 优点∶快速上手,同时画风简洁明了,突出重 点,能够快速的让用户了解内容 缺点:过于简洁,运用的也比较普遍,视觉上难以让用户记住 2.肌理插…...

Vue 3.0中Treeshaking特性是什么?
一、是什么 Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Dead code elimination 简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码 如果把代码打包比作制作蛋糕,传统的方式是把鸡…...
SQL union all的使用
背景: 公司业务开发需要将两个取出两个相同表结构(原料、辅料)的数据,组成一个新视图,使用了UNION ALL SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GOCREATE view vw_rawmaterial_ny_list as ( select id,ccode,cc…...

docker 安装 Centos7
1. 从docker 安装 Centos7 查看有哪些 centos7 系统:docker search centos72. 安装 centos7 docker pull docker.io/ansible/centos7-ansible3.使用镜像创建容器 docker run -itd -p 8022:22 --namevm01 -v /bodata:/bodata -h vm01 --privilegedtrue 688353a31…...

Kubernetes技术与架构-Ingress
Ingress是一个流量网关,其根据配置的URI路径路由规则,为运行在Kubernetes集群中的Service分发流量,从系统架构设计的角度看,Ingress位于Service的上层,本文主要描述Ingress的基本使用方式。 如上所示,clien…...

基于Java的文物管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...
uniapp图片加水印
1、uniapp加水印 1.1、创建画布容器 <canvas class"watermark-canvas" id"watermark-canvas" canvas-id"watermark-canvas":style"{ width: canvasWidth, height: canvasHeight }" /> 1.2、获取水印内容 async getLocation(…...

react中JSX基础与useState的基本使用 + 评论显示删除需求案例
参考视频:https://www.bilibili.com/video/BV1ZB4y1Z7o8/?p3&spm_id_frompageDriver&vd_source5c584bd3b474d579d0bbbffdf0437c70 如果没有安装create-react-app需要先全局安装 命令:npm i -g create-react-app1.快速搭建开发环境 create-re…...

【OpenCV实现鼠标绘图,轨迹栏做调色板,图像的基本操作】
文章目录 鼠标绘图轨迹栏做调色板图像的基本操作 鼠标绘图 在OpenCV中操作鼠标事件 函数:cv.setMouseCallback() 目的是在鼠标双击的地方画一个圆。首先,我们需要创建一个鼠标回调函数,该函数会在鼠标事件发生时执行。鼠标事件包括左键按下…...

2023年中国自动排气阀产业链、市场规模及存在问题分析]图[
自动排气阀是一种用于排除管道、容器或设备中累积的空气或气体的装置。在液体流动系统中,气体或空气可能会积聚在管道或容器中,影响流体流动、导致气锁和能效降低。自动排气阀的作用是在系统中的气体达到一定压力时,自动地释放气体࿰…...

服务器往浏览器推消息(SSE)应用
1,SSE 和 WebSocket 对比 SSE(服务器发送事件) SSE是一种基于HTTP的单向通信机制,用于服务器向客户端推送数据。它的工作原理如下: 建立连接:客户端通过发送HTTP请求与服务器建立连接。在请求中ÿ…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...