react-router-dom v. 6.4.2 не разрешает history.listen, как указано в приведенном ниже примере кода. Это для mfe с федерацией модулей.
В примере кода с использованием history.listen, если щелкнуть ссылку на удаленном устройстве (загруженном как mfe), то текущий путь истории памяти (теперь маршрутизатор памяти) будет обновлен. Затем он вызовет onNavigate, чтобы сообщить хост-контейнеру, который использует историю браузера (теперь маршрутизатор браузера), что текущий путь изменился.
Предыдущие предложения заключались в использовании UNSAFE_NavigationContext, useHistory, нестабильной_HistoryRouter, импорте {...} из «истории» и т. д. По-видимому, эти предыдущие методы были временными средствами миграции с версии 5 на версию 6.3, а с версией 6.4+ теперь устарели. новых API данных в 6.4. Смотрите здесь
мы не собираемся поддерживать пользовательские истории в будущем. Этот API находится здесь в качестве помощи при миграции. Мы рекомендуем удалить пользовательские истории из вашего приложения.
Дополнительно от сопровождающих RRD:
Мы рекомендуем обновить ваше приложение, чтобы использовать один из новых маршрутизаторов версии 6.4.
После поиска здесь, а также в открытых и закрытых проблемах на remix-RRD я не смог найти работающее решение, основанное на вышеизложенном, для замены history.listen, .push или .location на новые API данных (маршрутизаторы) с использованием createBrowserRouter или createMemoryRouter, как указано здесь
На странице react-router-dom есть много открытых вопросов, касающихся этого варианта использования.
Оригинальный 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.info('defaultRouter')
}
}
// We are running through container
// and we should export the mount function
export { mount }
Оригинальный marketing/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} />
}
Замена container/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.info('The container noticed navigation in Marketing')
},
})
})
return <div ref = {ref} />
}
Ищете решение для замены history.listen, history.location и history.push, которое работает с новыми API данных v6.4?
По словам одного из сопровождающих react-router-dom, это связано с новым API навигации для современной маршрутизации на стороне клиента для одностраничных приложений. developer.chrome.com/docs/web-platform/navigation-api
Да — точно так же, как axios мог бы отбросить запросы XHR для выборки, но внесение нежелательных изменений в основную библиотеку SPA в лучшем случае сомнительно. Вот как это должно быть обработано: github.com/axios/axios/issues/1219. Я отвлекся!
history.push
был заменен функцией navigate
через хук useNavigate
, доступ к location
осуществляется через хук useLocation
, а history.listen
теперь не существует. Что вам нужно от функции listen
, action
и location
?
Возможно оба? В хосте «Контейнер» у меня есть функция обратного вызова onNavigate
. Эта функция обратного вызова принимается удаленным «Маркетингом». Когда навигация происходит в пульте «Маркетинг», мне нужно вызвать onNavigate
внутри пульта. Пульт использует createMemoryBrowser()
, и когда путь меняется, мне нужно вызвать функцию onNavigate
и напечатать оператор console.info
, показанный в строке 10 Replacement container/src/components/MarketingApp.tsx from host (in progress)
. Хозяин «Контейнер» использует createBrowserRouter
. И хост, и удаленный компьютер используют новые маршрутизаторы данных.
Так что вам просто нужно знать, когда маршрут меняется?
Да! Внутри функции mount
в bootstrap.tsx.
@Optionwiz Вы нашли исправление для этого обновления маршрутизатора? если да, то не могли бы вы поделиться ссылкой на репозиторий. Я слишком борюсь с этим
Один из сопровождающих RRD только что опубликовал новую деталь реализации для замены history.listen для версии 6.4+. Смотрите router.subscribe()
ниже.
let router = createBrowserRouter(...);
// If you need to navigate externally, instead of history.push you can do:
router.navigate('/path');
// And instead of history.replace you can do:
router.navigate('/path', { replace: true });
// And instead of history.listen you can:
router.subscribe((state) => console.info('new state', state));
К сожалению, новая реализация также нестабильна и считается реализацией бета-тестирования.
А теперь о плохих новостях 😕 . Так же, как нестабильной_HistoryRouter мы также рассматривать этот тип внешней навигации и подписки как нестабильный, поэтому мы не задокументировали это и почему мы отметили все API-интерфейсы маршрутизатора как @internal PRIVATE - НЕ ИСПОЛЬЗОВАТЬ в JSDoc/машинопись. Это не значит, что они всегда будут нестабильными, но поскольку это не обычное ожидаемое использование маршрутизатора, мы все еще следя за тем, чтобы этот тип внешней навигации не создавать проблемы (и мы вполне уверены, что это не введение useSyncExternalStore в реакцию 18!)
Если этот тип навигации необходим для вашего приложения и вам нужен замена для нестабильного_HistoryRouter при использовании RouterProvider тогда мы рекомендуем вам использовать методы router.navigate и router.subscribe и помогите нам провести бета-тестирование подхода! Пожалуйста, не стесняйтесь открывать новый GH проблемы, если вы столкнетесь с каким-либо использованием этого подхода, и мы будем использовать их для помогите нам сделать призыв к продвижению этого к будущему стабильному выпуску.
О боже, было бы неплохо, если бы сопровождающие RRD были немного более открытыми в отношении того, что я считаю довольно серьезными критическими изменениями. Они коренным образом меняют поведение, на которое стали полагаться разработчики. Конечно, они говорят, что они «нестабильны» и являются деталями внутренней реализации… но очевидно, что вариант использования существует, иначе многим из нас не пришлось бы ломать голову над тем, как воспроизвести поведение, к которому мы привыкли.
Вот что происходит, когда классическая часть открытого исходного кода незаметно переносится на обслуживание коммерческой организации.