繁体   English   中英

升级 React-router-dom v6 多个错误

[英]Upgrading React-router-dom v6 multiple errors

所以我一直在为一个不是我的项目升级模块,所以我不熟悉代码,当我尝试从 5.2.0 ❯ 6.3.0 升级 react-router-dom 时,我偶然发现了路由的实现方式. 替换了reprecated模块后,我收到多个错误,基本上说路由没有正确定义,但我尝试了所有方法,但无法正常工作。 这些是我加载页面时遇到的错误。

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Matched leaf route at location "/" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.

Uncaught Error: [Navigate] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>

这是处理路由的应用程序组件。

import routes from './App.routes'
import { lazy } from 'react'
import AppContainer from './containers/app'
import AuthContainer from './containers/auth'
import { Route, Routes, Navigate, useNavigate, useRoutes } from 'react-router-dom'
import { ModalError } from './components/ModalError/ModalError'
import { EnvironmentProvider } from './contexts/Environment'
import { useDispatch, useSelector } from 'react-redux'
import { hiddenError } from './store/slices/health'
import { FullScreenLoading } from './components/FullScreenLoading/FullScreenLoading'
import { Suspense, useEffect, useState } from 'react'
import * as actionsProfile from './store/slices/profile'
import { firebaseInit, getValuesRemoteConfig } from './service/firebase'
import { setSecret } from './service/cryptojs'
import axios from './config/axios'
import Maintenance from './pages/common/maintenance/maintenance'
import { googleAnalyticsRegisterView } from './components/GoogleAnalytics/GoogleAnalytics'

const App = () => {
  const [isLoadingConfig, setIsLoadingConfig] = useState(true)

  const [configBeSetted, setConfigBeSetted] = useState(false)

  const [appInitialized, setAppInitialized] = useState(false)

  const [configuration, setConfiguration] = useState({
    isMaintenanceWebMessage: null,
    isMaintenanceWeb: null,
    apiUrlRemote: null,
    timeoutRemote: null,
    secretRemote: null,
    publicWebUrl: null,
    publicTurnsUrl: null,
    successRedirectUri: null,
    errorRedirectUri: null,
    googlePlayUrl: null,
    appStoreUrl: null,
  })

  const token = JSON.parse(localStorage.getItem('tsu'))?.token

  const { showError } = useSelector((state) => state.health.health)

  const dispatch = useDispatch()

  useEffect(() => {
    firebaseInit(async (app) => {
      const config = await getValuesRemoteConfig(app)

      //Production
      axios.defaults.baseURL = config.apiUrlRemote
      axios.defaults.timeout = config.timeoutRemote
      setSecret(config.secretRemote)

      setConfiguration(config)

      setIsLoadingConfig(false)

      setConfigBeSetted(true)

      setAppInitialized(true)

      token && dispatch(actionsProfile.getProfileData())
    })
  }, [token])

  if (isLoadingConfig || !configBeSetted) return <FullScreenLoading />

  if (configBeSetted && configuration?.isMaintenanceWeb) {
    return (
      <EnvironmentProvider config={configuration}>
        <Maintenance message={configuration?.isMaintenanceWebMessage} />
      </EnvironmentProvider>
    )
  }
  return (
    <Suspense fallback={<FullScreenLoading />}>
      <EnvironmentProvider config={configuration}>
        {showError && <ModalError onClose={() => dispatch(hiddenError())} />}
        <Routes>
          {routes
            .map((route) => ({
              ...route,
              component: appInitialized
                ? googleAnalyticsRegisterView(route.component)
                : route.component,
            }))
            .map((route, i) => {
              const Container = route.private ? AppContainer : AuthContainer
              return (
                <Route
                  key={i}
                  path={route.path}
                  exact={route.exact}
                  render={(props) => {
                    return (
                      <Container key={i}>
                        <route.component {...props} />
                      </Container>
                    )
                  }}
                />
              )
            })}
          <Navigate to={token ? '/home' : '/'} />
        </Routes>
      </EnvironmentProvider>
    </Suspense>
  )
}

export default App

这是路由组件。

import { lazy } from 'react'
import { googleAnalyticsRegisterView } from './components/GoogleAnalytics/GoogleAnalytics'

const Home = lazy(() => import('./pages/app/home'))
const MyData = lazy(() => import('./pages/app/my-data'))
const ClientAttention = lazy(() => import('./pages/app/client-atention'))
const PhoneAssistance = lazy(() => import('./pages/app/phone-assitance'))
const InsuranceDetails = lazy(() => import('./pages/app/insurance-details'))
const FeePayment = lazy(() => import('./pages/app/fee-payment'))
const MyInsurance = lazy(() => import('./pages/app/my-insurance'))
const ReportClaim = lazy(() => import('./pages/app/report-claim'))
const Notifications = lazy(() => import('./pages/app/notifications'))
const Login = lazy(() => import('./pages/auth/login'))
const RecoverPassword = lazy(() => import('./pages/auth/recover-password'))
const Register = lazy(() => import('./pages/auth/register'))
const NewPassword = lazy(() => import('./pages/auth/new-password'))
const FrecuentlyQuestions = lazy(() =>
  import('./pages/app/frecuently-question')
)
const ProofOfPayment = lazy(() =>
  import('./pages/common/proof-of-payment/ProofOfPayment')
)
const SuccessPayment = lazy(() =>
  import('./pages/common/payments/success/SuccessPayment')
)
const ErrorPayment = lazy(() =>
  import('./pages/common/payments/error/ErrorPayment')
)
const AppRedirect = lazy(() => import('./pages/auth/app-redirect'))

export const privateRoutes = [
  '/home',
  '/my-data',
  '/phone-assistance',
  '/client-attention',
  '/details',
  '/fee-payment',
  '/my-insurances',
  '/report-claim',
  '/notifications',
  '/faqs',
]

export const publicRoutes = [
  '/',
  '/recover-password',
  '/new-password',
  '/register',
  '/certifacates',
  '/app-redirect',
]

const routes = [
  {
    component: SuccessPayment,
    path: '/success-payment',
    exact: true,
    private: !!localStorage.getItem('tsu'),
  },
  {
    component: ErrorPayment,
    path: '/error-payment',
    exact: true,
    private: !!localStorage.getItem('tsu'),
  },
  {
    component: Login,
    path: '/',
    exact: true,
    private: false,
  },
  {
    component: RecoverPassword,
    path: '/recover-password',
    exact: true,
    private: false,
  },
  {
    component: NewPassword,
    path: '/new-password',
    exact: true,
    private: false,
  },
  {
    component: Register,
    path: '/register',
    exact: true,
    private: false,
  },
  {
    component: Home,
    path: '/home',
    exact: true,
    private: true,
  },
  {
    component: MyData,
    path: '/my-data',
    exact: true,
    private: true,
  },
  {
    component: PhoneAssistance,
    path: '/phone-assistance',
    exact: true,
    private: true,
  },
  {
    component: ClientAttention,
    path: '/client-attention',
    exact: true,
    private: true,
  }
]

export default routes

这是渲染应用程序的主要组件

import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import GlobalStyles from './global-styles'
import ErrorBoundary from './components/ErrorBoundary/ErrorBoundary'
import store, { history } from './store'
import { HashRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import * as Sentry from '@sentry/react'
import { BrowserTracing } from '@sentry/tracing'

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_KEY,
  integrations: [new BrowserTracing()],
  tracesSampleRate: 1.0,
})

const container = document.getElementById('root')
const root = createRoot(container) // createRoot(container!) if you use TypeScript
root.render(
  <React.StrictMode>
    <ErrorBoundary>
      <Provider store={store}>
        <HashRouter ref={history}>
          <GlobalStyles />
          <App />
        </HashRouter>
      </Provider>
    </ErrorBoundary>
  </React.StrictMode>
)

历史被导出,如 export const history = createRef();

我知道那个 ref 属性或 HashRouter 有一些东西,但在文档中我找不到它。

第一期

警告:不能给 Function 组件提供参考。 尝试访问此 ref 将失败。 你的意思是使用 React.forwardRef() 吗?

这确实似乎与HashRouter组件有关。 您可以查看类型声明并看到HashRouter不使用ref道具。

 declare function HashRouter( props: HashRouterProps ): React.ReactElement; interface HashRouterProps { basename?: string; children?: React.ReactNode; window?: Window; }

并且还要检查源代码以验证它也没有转发任何 React ref。

 /** * A `<Router>` for use in web browsers. Stores the location in the hash * portion of the URL so it is not sent to the server. */ export function HashRouter({ basename, children, window }: HashRouterProps) { let historyRef = React.useRef<HashHistory>(); if (historyRef.current == null) { historyRef.current = createHashHistory({ window, v5Compat: true }); } let history = historyRef.current; let [state, setState] = React.useState({ action: history.action, location: history.location, }); React.useLayoutEffect(() => history.listen(setState), [history]); return ( <Router basename={basename} children={children} location={state.location} navigationType={state.action} navigator={history} /> ); }

Hashrouter在内部实例化并维护它自己的history引用。 要解决 ref 问题,只需删除 ref。

const container = document.getElementById('root');
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <ErrorBoundary>
      <Provider store={store}>
        <HashRouter>
          <GlobalStyles />
          <App />
        </HashRouter>
      </Provider>
    </ErrorBoundary>
  </React.StrictMode>
);

如果您需要使用自定义hashHistory object,请使用HistoryRouter并按照说明进行渲染。 确保将history@5安装为项目依赖项。

第 2 期

位置“/”的匹配叶路由没有元素。 这意味着默认情况下它将呈现具有 null 值的<Outlet /> ,从而导致“空”页面。

Route组件 API 从 v5 到 v6 发生了显着变化。 Route组件将其所有内容呈现在采用React.ReactNode (又名 JSX)的单个element prop 上。 现在不再使用exact ,因为所有路由总是完全匹配的。

{routes
  .map((route, i) => {
    const Container = route.private ? AppContainer : AuthContainer;
    const Component = appInitialized
      ? googleAnalyticsRegisterView(route.component)
      : route.component;
    return (
      <Route
        key={i}
        path={route.path}
        element={(
          <Container key={i}>
            <Component />
          </Container>
        )}
      />
    )
  })
}

这也意味着不再有任何路线道具。 路由组件需要使用 React 钩子:

  • 使用Navigate 访问替换historynavigate useNavigate
  • useParams访问替换match.params的路径参数
  • 使用Location访问location useLocation

第 3 期

未捕获的错误:[Navigate] 不是<Route>组件。 <Routes>的所有子组件必须是<Route><React.Fragment>

只有RouteReact.FragmentRoutes组件的有效子级。 如果要重定向该Navigate组件,仍需要在路由上呈现。 指定replace属性,以便导航操作是 REPLACE 而不是 PUSH。

<Route
  path="*"
  element={<Navigate to={token ? '/home' : '/'} replace />}
/>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM