react-router-dom v6版本实现Tabs路由缓存切换

news2025/1/16 13:40:54

目录

文章目录

概要

         效果

完整代码

概要

摆了半年摊,好久没写代码了,今天有人问我怎么实现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>
    <script
        src="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 } = React
        const { ProLayout } = ProComponents
        const { createHashRouter, useOutlet, Navigate, RouterProvider, useLocation, Link, useNavigate } = ReactRouterDOM
        const { Tabs, Button, Space, Row, Col, Input, } = antd

        const 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({}).current
            const [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>

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

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

相关文章

工业交换机定制化解决方案:光路科技的工业PoE交换机

PoE交换机能代替普通工业交换机使用吗&#xff1f; Power over Ethernet (PoE)交换机和普通工业交换机在某些情况下可以互相替代&#xff0c;但有些情况下则不太适合。PoE交换机具有额外的供电能力&#xff0c;用于同时为网络设备提供数据和电力。如果您的应用中只需要传输数据…

python web开发(四): Bootstrap

1.初步了解 别人已经写好的CSS样式&#xff0c;我们可以直接引用 下载 Link-BootStrap 解压&#xff0c;并放入到当前项目中 引用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</tit…

IT售前“楠“知识之这!就是售前-尚文网络xUP楠哥

进Q群11372462领取专属报名福利 &#xff01;&#xff01;&#xff01; # 何为售前工程师 售前工程师在一个IT信息化团队中起到了呈上启下的绝对重要作用&#xff01;站在销售团队的视角&#xff0c;需要售前工程师从技术维度支持销售业务的开展&#xff1b;站在对立面用户的…

一些bug总结

今天被几个小问题和bug折磨了一天&#xff0c;来总结一下… 权限问题 用vscode连接服务器&#xff0c;如果是在root用户连接的情况下新建的文件/文件夹&#xff0c;然后切换到别的用户的时候去写的代码 可能会遇到各种问题 解决方案是更改文件或文件夹的所有权。这可以通过使用…

pymysql连接Mariadb/Mysql出现错误(配置正确情况下)解决办法

场景&#xff1a;在kali中使用python中pymysql对Mariadb进行连接&#xff0c;在整个过程中配置全部正确&#xff0c;但是就是无法进行连接&#xff0c;提示结果如下&#xff1a; Access denied for user rootlocalhost解决办法&#xff1a;进入数据库中&#xff0c;将默认密码…

一步一步分析ChatGPT,1 粘性,2 传染性, 3 双边网络效应

请按照以下三个维度一步一步分析ChatGPT&#xff0c;1 粘性&#xff0c;2 传染性&#xff0c; 3 双边网络效应&#xff0c;比如亚马逊的买家和商家的关系 ChatGPT的分析 1.1. 粘性 (Stickiness) 定义&#xff1a; 粘性是指产品或服务对用户的吸引力&#xff0c;即用户在使用…

番外8.1 配置+管理文件系统

Task01: Linux 文件系统结构&#xff1b; 可以进行Linux操作系统的文件权限管理与方式切换&#xff0c;可以应用磁盘与文件权限管理工具&#xff1b; 01&#xff1a;常见文件系统类型&#xff08;Ext4[rhel6默认文件管理系统], 存储容量1 EB1073741824 GB; XFS[rhel 7/8默认的文…

HugeGraph 部署和Hubble1.0.0的数据导入Bug修复

背景 HugeGraph 安装部署了最新版本1.0.0&#xff0c;发现它的 Web 工具 Hubble 有一个大 Bug。数据导入的时候&#xff0c;配置节点属性映射这个选项时&#xff0c;下拉框只有一个选项&#xff0c;但实际上&#xff0c;元数据配置中的属性有3个&#xff0c;这个 Bug 是怎么产…

黑客(自学方法)技术——网络安全

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

项目管理之5W2H项目定义法

在项目管理中&#xff0c;为了确保项目的成功实施&#xff0c;项目团队需要全面了解项目的各个方面&#xff0c;并制定相应的计划和措施。为此&#xff0c;可以采用一种被称为5W2H的项目定义方法。这种方法可以帮助项目团队更好地了解项目的需求、干系人、实施地点、交付物、时…

环境变量【使用命令行参数引出环境变量】

前提&#xff1a;命令行参数 大家在写C/C程序的时候肯定见过下面这种情况&#xff1a; main函数里面携带的参数&#xff0c;平常写代码过程中很少用到这两个参数&#xff0c;接下来我们就研究一下 我们也不知道 指针数组argv里面到底保存的是什么&#xff0c;也不知道这个a…

Java实现业务异步的几种方案

背景&#xff1a; 在java中异步线程很重要&#xff0c;比如在业务流处理时&#xff0c;需要通知硬件设备&#xff0c;发短信通知用户&#xff0c;或者需要上传一些图片资源到其他服务器这种耗时的操作&#xff0c;在主线程里处理会阻塞整理流程&#xff0c;而且我们也不需要等…

软考系列(系统架构师)- 2017年系统架构师软考案例分析考点

试题一 软件架构&#xff08;质量属性效用树、架构风险、敏感点、权衡点&#xff09; 系统架构风险、敏感点和权衡点的定义 【问题2】&#xff08;13分&#xff09; 在架构评估过程中&#xff0c;需要正确识别系统的架构风险、敏感点和权衡点&#xff0c;并进行合理的架构决策…

ubuntu20.04 nvidia显卡驱动掉了,变成开源驱动,在软件与更新里选择专有驱动,下载出错,调整ubuntu镜像源之后成功修复

驱动配置好&#xff0c;环境隔了一段时间&#xff0c;打开Ubuntu发现装好的驱动又掉了&#xff0c;软件与更新 那里&#xff0c;附加驱动&#xff0c;显示开源驱动&#xff0c;命令行输入 nvidia-smi 命令查找不到驱动。 点击上面的 nvidia-driver-470&#xff08;专有&#x…

Spring 国际化:i18n

文章目录 i18n概述Java国际化Spring6国际化MessageSource接口使用Spring6国际化 i18n概述 国际化也称作i18n&#xff0c;其来源是英文单词 internationalization的首末字符i和n&#xff0c;18为中间的字符数。由于软件发行可能面向多个国家&#xff0c;对于不同国家的用户&…

【AI视野·今日CV 计算机视觉论文速览 第268期】Mon, 16 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Mon, 16 Oct 2023 Totally 61 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Vision-by-Language for Training-Free Compositional Image Retrieval Authors Shyamgopal Karthik, Karsten Roth, Massi…

SpringCloud-Nacos

一、介绍 &#xff08;1&#xff09;作为服务注册中心和配置中心 &#xff08;2&#xff09;等价于&#xff1a;EurekaConfigBus &#xff08;3&#xff09;nacos集成了ribbon&#xff0c;支持负载均衡 二、安装 &#xff08;1&#xff09;官网 &#xff08;2&#xff09; …

linux加密安全和时间同步

sudo实现授权 添加 vim /etc/sudoers luo ALL(root) /usr/bin/mount /deb/cdrom /mnt/ test ALL(root:ALL) ALL 在所有主机上 提权为root用户&#xff0c; 可以执行所有命令 户"test"被授权以"root"用户身份在任意主机上执行任意命令 切换luo用户使用 su…

微信小程序之会议OA系统首页布局搭建与Mock数据交互

目录 前言 一、Flex 布局&#xff08; 分类 编程技术&#xff09; 1、Flex布局是什么&#xff1f; 2、基本概念 3、容器的属性 3.1 flex-direction属性 3.2 flex-wrap属性 3.3 flex-flow 3.4 justify-content属性 3.5 align-items属性 3.6 align-content属性 4、项目…

实现mnist手写数字识别

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/Nb93582M_5usednAKp_Jtw) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)** >- **&#x1f680;…