React Router Dom v6.4 не разрешает history.listen (предыдущие предложения устарели) с помощью createBrowserRouter или createMemoryRouter

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?

Вот что происходит, когда классическая часть открытого исходного кода незаметно переносится на обслуживание коммерческой организации.

OFRBG 22.10.2022 20:31

По словам одного из сопровождающих react-router-dom, это связано с новым API навигации для современной маршрутизации на стороне клиента для одностраничных приложений. developer.chrome.com/docs/web-platform/navigation-api

Optionwiz 22.10.2022 23:55

Да — точно так же, как axios мог бы отбросить запросы XHR для выборки, но внесение нежелательных изменений в основную библиотеку SPA в лучшем случае сомнительно. Вот как это должно быть обработано: github.com/axios/axios/issues/1219. Я отвлекся!

OFRBG 23.10.2022 01:20
history.push был заменен функцией navigate через хук useNavigate, доступ к location осуществляется через хук useLocation, а history.listen теперь не существует. Что вам нужно от функции listen, action и location?
Drew Reese 26.10.2022 08:36

Возможно оба? В хосте «Контейнер» у меня есть функция обратного вызова onNavigate. Эта функция обратного вызова принимается удаленным «Маркетингом». Когда навигация происходит в пульте «Маркетинг», мне нужно вызвать onNavigate внутри пульта. Пульт использует createMemoryBrowser(), и когда путь меняется, мне нужно вызвать функцию onNavigate и напечатать оператор console.info, показанный в строке 10 Replacement container/src/components/MarketingApp.tsx from host (in progress). Хозяин «Контейнер» использует createBrowserRouter. И хост, и удаленный компьютер используют новые маршрутизаторы данных.

Optionwiz 26.10.2022 23:16

Так что вам просто нужно знать, когда маршрут меняется?

Drew Reese 28.10.2022 07:44

Да! Внутри функции mount в bootstrap.tsx.

Optionwiz 28.10.2022 15:56

@Optionwiz Вы нашли исправление для этого обновления маршрутизатора? если да, то не могли бы вы поделиться ссылкой на репозиторий. Я слишком борюсь с этим

SDK 06.01.2023 02:53
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
3
8
1 003
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Один из сопровождающих 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 были немного более открытыми в отношении того, что я считаю довольно серьезными критическими изменениями. Они коренным образом меняют поведение, на которое стали полагаться разработчики. Конечно, они говорят, что они «нестабильны» и являются деталями внутренней реализации… но очевидно, что вариант использования существует, иначе многим из нас не пришлось бы ломать голову над тем, как воспроизвести поведение, к которому мы привыкли.

Drew Reese 04.11.2022 08:07

Другие вопросы по теме