reactjs React Router Dom v6.4不允许使用历史记录,请使用createBrowserRouter或createMemoryRouter侦听(先前的建议已弃用)

wecizke3  于 2022-11-04  发布在  React
关注(0)|答案(1)|浏览(171)

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一起工作?

tzdcorbm

tzdcorbm1#

RRD的一个维护者刚刚发布了一个新的实现细节来替换v6.4+的history.listen。请参见下面的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.log('new state', state));

不幸的是,新的实现也不稳定,被认为是beta测试实现。
现在,坏消息来😕了。就像unstable_HistoryRouter一样,我们也认为这种类型的外部导航和订阅是不稳定的,这就是为什么我们没有记录这一点,也是为什么我们在JSDoc/Typescript中将所有路由器API标记为@internal PRIVATE - DO NOT USE。这并不是说它们永远不稳定,但由于这不是路由器的正常预期用途,我们仍在确保这种外部导航不会带来问题(而且我们相当有信心在react 18中引入useSyncExternalStore后不会出现问题!)
如果您的应用需要这种类型的导航,并且在使用RouterProvider时需要替换unstable_HistoryRouter,那么我们建议您使用router.navigate和router.subscribe方法,并帮助我们对该方法进行beta测试!如果您使用该方法遇到任何问题,请随时打开新的GH问题,我们将使用这些问题来帮助我们做出决定,将其推向未来的稳定版本。

相关问题