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

 更新时间:2023年10月19日 15:51:08   作者:很甜的西瓜  
今天有人问我怎么实现React-Router-dom类似标签页缓存,很久以前用的是react-router v5那个比较容易实现,v6变化挺大,但了解react的机制和react-router的机制就容易了,本文介绍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" rel="external nofollow" >
    <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>

到此这篇关于react-router-dom v6版本实现Tabs路由缓存切换的文章就介绍到这了,更多相关react-router-dom路由缓存切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React中使用dnd-kit实现拖曳排序功能

    React中使用dnd-kit实现拖曳排序功能

    在这篇文章中,我将带着大家一起探究React中使用dnd-kit实现拖曳排序功能,由于前阵子需要在开发 Picals 的时候,需要实现一些拖动排序的功能,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • React Redux应用示例详解

    React Redux应用示例详解

    这篇文章主要介绍了如何在React中直接使用Redux,目前redux在react中使用是最多的,所以我们需要将之前编写的redux代码,融入到react当中去,本文给大家详细讲解,需要的朋友可以参考下
    2022-11-11
  • 基于react封装一个通用可编辑组件

    基于react封装一个通用可编辑组件

    前段时间接到这样一个需求,需要封装一个组件实现可编辑,这个到底有多通用呢,就是需要在普通的文字展示包括表格,列表等等,所以本文将给大家介绍如何基于react封装一个通用可编辑组件,需要的朋友可以参考下
    2024-02-02
  • react使用axios实现上传下载功能

    react使用axios实现上传下载功能

    这篇文章主要为大家详细介绍了react使用axios实现上传下载功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 详解React Native 采用Fetch方式发送跨域POST请求

    详解React Native 采用Fetch方式发送跨域POST请求

    这篇文章主要介绍了详解React Native 采用Fetch方式发送跨域POST请求,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • 详解如何在React中有效地监听键盘事件

    详解如何在React中有效地监听键盘事件

    React是一种流行的JavaScript库,用于构建用户界面,它提供了一种简单而灵活的方式来创建交互式的Web应用程序,在React中,我们经常需要监听用户的键盘事件,以便根据用户的输入做出相应的反应,本文将向您介绍如何在React中有效地监听键盘事件,并展示一些常见的应用场景
    2023-11-11
  • 深入理解React中何时使用箭头函数

    深入理解React中何时使用箭头函数

    对于刚学前端的大家来说,对于React中的事件监听写法有所疑问很正常,特别是React中箭头函数使用这块,下面这篇文章主要给大家深入的讲解了关于React中何时使用箭头函数的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-08-08
  • 基于Node的React图片上传组件实现实例代码

    基于Node的React图片上传组件实现实例代码

    本篇文章主要介绍了基于Node的React图片上传组件实现实例代码,非常具有实用价值,需要的朋友可以参考下
    2017-05-05
  • 简化Cocos和Native交互利器详解

    简化Cocos和Native交互利器详解

    这篇文章主要为大家介绍了简化Cocos和Native交互利器详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • React-View-UI组件库封装Loading加载中源码

    React-View-UI组件库封装Loading加载中源码

    这篇文章主要介绍了React-View-UI组件库封装Loading加载样式,主要包括组件介绍,组件源码及组件测试源码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06

最新评论