react-router-dom版本6.4.2不允许history.listen,如下面的代码示例中所引用的。这是针对具有模块联合的mfe。
在使用history.listen的代码示例中,如果在remote(加载为mfe)中单击了一个链接,那么内存历史记录(内存路由器现在)的当前路径将被更新。然后它将调用onNavigate来告诉正在使用浏览器历史记录(浏览器路由器现在)的主机容器当前路径已经更改。
之前的建议是使用UNSAFE_NavigationContext、useHistory、unstable_HistoryRouter、import {...} from 'history'等。显然,这些之前的方法是从v5到v6.3的临时迁移辅助工具,而对于v6.4及以上版本,这些方法现在已被弃用,取而代之的是6.4中的新数据API。请参见here
我们不打算支持自定义历史记录向前移动。此API在此用作迁移辅助工具。我们建议从您的应用程序中删除自定义历史记录。
此外,from the maintainers of RRD:
我们建议更新您的应用程序以使用6.4版中的新路由器。
在搜索了这里以及remix-RRD上的未决和已关闭问题之后,我无法找到一个可行的解决方案,基于上述内容,使用createBrowserRouter或createMemoryRouter将history.listen、.push或.location替换为新的数据API(路由器),如这里所引用的
react-router-dom页面上有许多与this use case相关的未决问题。
来自远程的原始marketing/src/bootstrap.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import { createMemoryHistory, createBrowserHistory } from 'history' <= Not Supported
import App from './App'
let root: { render: (arg0: JSX.Element) => void } | null = null
// Mount function to start up the app
const mount = (el: any, { onNavigate, defaultHistory, initialPath }: any) => {
if (!el) {
root = null
return
}
// If defaultHistory in development and isolation use BrowserHistory
const history =
defaultHistory ||
// Otherwise use MemoryHistory and initial path from container
createMemoryHistory({
initialEntries: [initialPath],
})
if (onNavigate) {
history.listen(onNavigate) <= Not Supported
}
root = root ? root : createRoot(el)
root.render(<App history={history} />)
return {
onParentNavigate({ pathname: nextPathname }: any) {
const { pathname } = history.location <= Not Supported
if (pathname !== nextPathname) {
history.push(nextPathname) <= Not Supported
}
},
}
}
// If we are in development and in isolation,
// call mount immediately
if (process.env.NODE_ENV === 'development') {
const devRoot = document.querySelector('#_marketing-dev-root')
if (devRoot) {
mount(devRoot, { defaultHistory: createBrowserHistory() })
}
}
// We are running through container
// and we should export the mount function
export { mount }
远程替换marketing/src/bootstrap.tsx(正在进行)
import React from 'react'
import { createRoot } from 'react-dom/client'
import {
createBrowserRouter,
createMemoryRouter,
} from 'react-router-dom'
import App from './App'
import ErrorPage from './pages/ErrorPage'
import Landing from './components/Landing'
import Pricing from './components/Pricing'
let root: { render: (arg0: JSX.Element) => void } | null = null
const routes = [
{
path: '/',
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <Landing />,
errorElement: <ErrorPage />,
},
{
path: 'pricing',
element: <Pricing />,
errorElement: <ErrorPage />,
},
],
},
]
// Mount function to start up the app
const mount = (
el: Element,
{
onNavigate,
defaultRouter,
}: {
onNavigate: (() => void) | null
defaultRouter: any
},
): unknown => {
if (!el) {
root = null
return
}
// if in development and isolation, use browser router. If not, use memory router
const router = defaultRouter || createMemoryRouter(routes)
if (onNavigate) {
router.listen(onNavigate) // There is no history.listen anymore. router.listen is not a function
}
root = root ? root : createRoot(el)
root.render(<App router={router} />)
}
// If we are in development and in isolation,
// call mount immediately
if (process.env.NODE_ENV === 'development') {
const devRoot = document.querySelector('#_marketing-dev-root')
if (devRoot) {
mount(devRoot, { defaultRouter: createBrowserRouter(routes) })
console.log('defaultRouter')
}
}
// We are running through container
// and we should export the mount function
export { mount }
来自远程的原始营销/src/App.tsx
import './MuiClassNameSetup'
import React from 'react'
import { Switch, Route, Router } from 'react-router-dom'
import Landing from './components/Landing'
import Pricing from './components/Pricing'
export default function _({ history }: any) {
return (
<div>
<Router history={history}>
<Switch>
<Route exact path="/pricing" component={Pricing} />
<Route path="/" component={Landing} />
</Switch>
</Router>
</div>
)
}
远程替换marketing/src/App.tsx(正在进行)
import './MuiClassNameSetup'
import React from 'react'
import {
RouterProvider,
} from 'react-router-dom'
export default function App({ router }: any) {
return <RouterProvider router={router} />
}
来自主机的原始容器/src/components/MarketingApp.tsx
import { mount } from 'marketing/MarketingApp'
import React, { useRef, useEffect } from 'react'
import { useHistory } from 'react-router-dom' <= Not Supported
export default function _() {
const ref = useRef(null)
const history = useHistory() <= Not Supported
useEffect(() => {
const { onParentNavigate } = mount(ref.current, {
initialPath: history.location.pathname,
onNavigate: ({ pathname: nextPathname }: any) => {
const { pathname } = history.location <= Not Supported
if (pathname !== nextPathname) {
history.push(nextPathname) <= Not Supported
}
},
})
history.listen(onParentNavigate) <= Not Supported
}, [history])
return <div ref={ref} />
}
从主机替换容器/src/components/MarketingApp.tsx(正在进行)
import { mount } from 'marketing/MarketingApp'
import React, { useRef, useEffect } from 'react'
export default function _() {
const ref = useRef(null)
useEffect(() => {
mount(ref.current, {
onNavigate: () => {
console.log('The container noticed navigation in Marketing')
},
})
})
return <div ref={ref} />
}
寻找一个解决方案来取代history.listen,history.location和history.push,并与新的v6.4数据api一起工作?
1条答案
按热度按时间tzdcorbm1#
RRD的一个维护者刚刚发布了一个新的实现细节来替换v6.4+的history.listen。请参见下面的
router.subscribe()
。不幸的是,新的实现也不稳定,被认为是beta测试实现。
现在,坏消息来😕了。就像unstable_HistoryRouter一样,我们也认为这种类型的外部导航和订阅是不稳定的,这就是为什么我们没有记录这一点,也是为什么我们在JSDoc/Typescript中将所有路由器API标记为@internal PRIVATE - DO NOT USE。这并不是说它们永远不稳定,但由于这不是路由器的正常预期用途,我们仍在确保这种外部导航不会带来问题(而且我们相当有信心在react 18中引入useSyncExternalStore后不会出现问题!)
如果您的应用需要这种类型的导航,并且在使用RouterProvider时需要替换unstable_HistoryRouter,那么我们建议您使用router.navigate和router.subscribe方法,并帮助我们对该方法进行beta测试!如果您使用该方法遇到任何问题,请随时打开新的GH问题,我们将使用这些问题来帮助我们做出决定,将其推向未来的稳定版本。