react-router-dom v6版本實(shí)現(xiàn)Tabs路由緩存切換功能
概要
擺了半年攤,好久沒寫代碼了,今天有人問我怎么實(shí)現(xiàn)React-Router-dom類似標(biāo)簽頁緩存。后面看了一下router的官網(wǎng)。很久以前用的是react-router v5那個(gè)比較容易實(shí)現(xiàn)。v6變化挺大,但了解react的機(jī)制和react-router的機(jī)制就容易了.
想做到切換標(biāo)簽保留頁面的內(nèi)容不變,就要了解react的機(jī)制
首先虛擬DOM的機(jī)制就是對比,如果找不到就會重新掛載,找到了就更新。
React-Router的機(jī)制就是匹配路徑,找到了就返回對應(yīng)的路由組件,找不到返回為null
思路就是v6版本提供了Outlet這個(gè)輸出子路元素的組件
一般我們會這樣寫:

但如果要實(shí)現(xiàn)標(biāo)簽的話,就不能這樣寫。但是切換路由,地址一變它就替換了原來的了。所以要做的就是保留所有打開的標(biāo)簽的子路由元素:
主要目的就是保留所有的元素,隱藏路由就行,這樣react diff時(shí),還是會找到對應(yīng)key的路由,這樣它只是會更新路由的組件,而不會重新掛載。
如這樣寫:


效果

完整代碼
新建一個(gè)html復(fù)制進(jìn)去就可以運(yùn)行了
<!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" 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>到此這篇關(guān)于react-router-dom v6版本實(shí)現(xiàn)Tabs路由緩存切換的文章就介紹到這了,更多相關(guān)react-router-dom路由緩存切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React+TS+IntersectionObserver實(shí)現(xiàn)視頻懶加載和自動播放功能
通過本文的介紹,我們學(xué)習(xí)了如何使用 React + TypeScript 和 IntersectionObserver API 來實(shí)現(xiàn)一個(gè)視頻播放控制組件,該組件具有懶加載功能,只有在用戶滾動頁面且視頻進(jìn)入視口時(shí)才開始下載視頻資源,需要的朋友可以參考下2023-04-04
react項(xiàng)目打包后點(diǎn)擊index.html頁面出現(xiàn)空白的問題
這篇文章主要介紹了react項(xiàng)目打包后點(diǎn)擊index.html頁面出現(xiàn)空白的問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
詳解在React-Native中持久化redux數(shù)據(jù)
這篇文章主要介紹了在React-Native中持久化redux數(shù)據(jù),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
react quill中圖片上傳由默認(rèn)轉(zhuǎn)成base64改成上傳到服務(wù)器的方法
這篇文章主要介紹了react quill中圖片上傳由默認(rèn)轉(zhuǎn)成base64改成上傳到服務(wù)器的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10

