[英]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
安装为项目依赖项。
位置“/”的匹配叶路由没有元素。 这意味着默认情况下它将呈现具有 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 钩子:
history
的navigate
useNavigate
useParams
访问替换match.params
的路径参数location
useLocation
未捕获的错误:[Navigate] 不是
<Route>
组件。<Routes>
的所有子组件必须是<Route>
或<React.Fragment>
只有Route
或React.Fragment
是Routes
组件的有效子级。 如果要重定向该Navigate
组件,仍需要在路由上呈现。 指定replace
属性,以便导航操作是 REPLACE 而不是 PUSH。
<Route
path="*"
element={<Navigate to={token ? '/home' : '/'} replace />}
/>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.