[英]Upgrading React-router-dom v6 multiple errors
So I've been upgrading modules for a project that is not mine so I'm not familiar with the code, when I tried upgrading react-router-dom from 5.2.0 ❯ 6.3.0 I stumbled with the way the routes are implemented.所以我一直在为一个不是我的项目升级模块,所以我不熟悉代码,当我尝试从 5.2.0 ❯ 6.3.0 升级 react-router-dom 时,我偶然发现了路由的实现方式. Having replaced the reprecated modules I get multiple errors that basically say the routes are not being defined correctly but I tried everything and I can't get it to work.
替换了reprecated模块后,我收到多个错误,基本上说路由没有正确定义,但我尝试了所有方法,但无法正常工作。 These are the errors I get when I load the page.
这些是我加载页面时遇到的错误。
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>
This is the app component that deals with the routes.这是处理路由的应用程序组件。
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
This is the routes component.这是路由组件。
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
And this is the main component where app is rendered这是渲染应用程序的主要组件
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>
)
History is exported like export const history = createRef();历史被导出,如 export const history = createRef();
I know there is something about that ref property or HashRouter but in the documentation I can't find it.我知道那个 ref 属性或 HashRouter 有一些东西,但在文档中我找不到它。
Warning: Function components cannot be given refs.
警告:不能给 Function 组件提供参考。 Attempts to access this ref will fail.
尝试访问此 ref 将失败。 Did you mean to use React.forwardRef()?
你的意思是使用 React.forwardRef() 吗?
This does indeed appear to be related to the HashRouter
component.这确实似乎与
HashRouter
组件有关。 You can look at the type declaration and see that the HashRouter
doesn't consume a ref
prop.您可以查看类型声明并看到
HashRouter
不使用ref
道具。
declare function HashRouter( props: HashRouterProps ): React.ReactElement; interface HashRouterProps { basename?: string; children?: React.ReactNode; window?: Window; }
And also inspect the source code to verify it also doesn't forward any React ref.并且还要检查源代码以验证它也没有转发任何 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} /> ); }
The Hashrouter
instantiates and maintains it's own history
reference internally. Hashrouter
在内部实例化并维护它自己的history
引用。 To fix the ref issue simply remove the ref.要解决 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>
);
If you have need to use a custom hashHistory
object then use the HistoryRouter and follow the instructions for rendering.如果您需要使用自定义
hashHistory
object,请使用HistoryRouter并按照说明进行渲染。 Be sure to have history@5
installed as a project dependency.确保将
history@5
安装为项目依赖项。
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.这意味着默认情况下它将呈现具有 null 值的
<Outlet />
,从而导致“空”页面。
The Route
component API changed significantly from v5 to v6. Route
组件 API 从 v5 到 v6 发生了显着变化。 The Route
components render all their content on a single element
prop taking a React.ReactNode
, aka JSX. Route
组件将其所有内容呈现在采用React.ReactNode
(又名 JSX)的单个element
prop 上。 exact
is no longer used now that all routes are always exactly matched.现在不再使用
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>
)}
/>
)
})
}
This also means there are no longer any route props.这也意味着不再有任何路线道具。 The routed components need to use the React hooks:
路由组件需要使用 React 钩子:
useNavigate
to access the navigate
function that replaced history
history
的navigate
useNavigate
useParams
to access path params that replaced match.params
useParams
访问替换match.params
的路径参数useLocation
to access the location
objectlocation
useLocation
Uncaught Error: [Navigate] is not a
<Route>
component.未捕获的错误:[Navigate] 不是
<Route>
组件。 All component children of<Routes>
must be a<Route>
or<React.Fragment>
<Routes>
的所有子组件必须是<Route>
或<React.Fragment>
Only Route
or React.Fragment
are valid children of the Routes
component.只有
Route
或React.Fragment
是Routes
组件的有效子级。 If you want to redirect that Navigate
component still needs to be rendered on a route.如果要重定向该
Navigate
组件,仍需要在路由上呈现。 Specify the replace
prop so the navigation action is a REPLACE instead of a PUSH.指定
replace
属性,以便导航操作是 REPLACE 而不是 PUSH。
<Route
path="*"
element={<Navigate to={token ? '/home' : '/'} replace />}
/>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.