mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
refactor(react): remove IonRedirect and update BREAKING.md for React Router v6
This commit is contained in:
113
BREAKING.md
113
BREAKING.md
@@ -66,12 +66,62 @@ Routes that contain nested routes or child `IonRouterOutlet` components need a `
|
||||
Route parameters are now accessed via the `useParams` hook instead of props:
|
||||
|
||||
```diff
|
||||
- import { RouteComponentProps } from 'react-router-dom';
|
||||
+ import { useParams } from 'react-router-dom';
|
||||
|
||||
- const MyComponent: React.FC<RouteComponentProps<{ id: string }>> = ({ match }) => {
|
||||
- const id = match.params.id;
|
||||
+ const MyComponent: React.FC = () => {
|
||||
+ const { id } = useParams<{ id: string }>();
|
||||
```
|
||||
|
||||
**RouteComponentProps Removed**
|
||||
|
||||
The `RouteComponentProps` type and its `history`, `location`, and `match` props are no longer available in React Router v6. Use the equivalent hooks instead:
|
||||
|
||||
- `history` -> `useNavigate` (see below) or `useIonRouter`
|
||||
- `match.params` -> `useParams` (covered above)
|
||||
- `location` -> `useLocation`
|
||||
|
||||
```diff
|
||||
- import { RouteComponentProps } from 'react-router-dom';
|
||||
+ import { useNavigate, useLocation } from 'react-router-dom';
|
||||
+ import { useIonRouter } from '@ionic/react';
|
||||
|
||||
- const MyComponent: React.FC<RouteComponentProps> = ({ history, location }) => {
|
||||
- history.push('/path');
|
||||
- history.replace('/path');
|
||||
- history.goBack();
|
||||
- console.log(location.pathname);
|
||||
+ const MyComponent: React.FC = () => {
|
||||
+ const navigate = useNavigate();
|
||||
+ const router = useIonRouter();
|
||||
+ const location = useLocation();
|
||||
+ // In an event handler or useEffect:
|
||||
+ navigate('/path');
|
||||
+ navigate('/path', { replace: true });
|
||||
+ router.goBack();
|
||||
+ console.log(location.pathname);
|
||||
```
|
||||
|
||||
**Exact Prop Removed**
|
||||
|
||||
The `exact` prop is no longer needed. React Router v6 routes match exactly by default. To match sub-paths, use a `/*` suffix on the path:
|
||||
|
||||
```diff
|
||||
- <Route path="/home" exact />
|
||||
+ <Route path="/home" />
|
||||
```
|
||||
|
||||
**Render Prop Removed**
|
||||
|
||||
The `render` prop has been replaced with the `element` prop:
|
||||
|
||||
```diff
|
||||
- <Route path="/foo" render={(props) => <Foo {...props} />} />
|
||||
+ <Route path="/foo" element={<Foo />} />
|
||||
```
|
||||
|
||||
**Programmatic Navigation**
|
||||
|
||||
The `useHistory` hook has been replaced with `useNavigate`:
|
||||
@@ -79,18 +129,71 @@ The `useHistory` hook has been replaced with `useNavigate`:
|
||||
```diff
|
||||
- import { useHistory } from 'react-router-dom';
|
||||
+ import { useNavigate } from 'react-router-dom';
|
||||
+ import { useIonRouter } from '@ionic/react';
|
||||
|
||||
- const history = useHistory();
|
||||
+ const navigate = useNavigate();
|
||||
+ const router = useIonRouter();
|
||||
|
||||
- history.goBack();
|
||||
+ navigate(-1);
|
||||
- history.push('/path');
|
||||
+ navigate('/path');
|
||||
|
||||
- history.replace('/path');
|
||||
+ navigate('/path', { replace: true });
|
||||
|
||||
- history.push('/path');
|
||||
+ navigate('/path');
|
||||
- history.goBack();
|
||||
+ router.goBack();
|
||||
```
|
||||
|
||||
For more information on migrating from React Router v5 to v6, refer to the [React Router v6 Upgrade Guide](https://reactrouter.com/en/main/upgrading/v5).
|
||||
**Custom History Prop Removed**
|
||||
|
||||
The `history` prop has been removed from `IonReactRouter`, `IonReactHashRouter`, and `IonReactMemoryRouter`. React Router v6's `BrowserRouter`, `HashRouter`, and `MemoryRouter` no longer accept custom `history` objects.
|
||||
|
||||
```diff
|
||||
- import { createBrowserHistory } from 'history';
|
||||
- const history = createBrowserHistory();
|
||||
- <IonReactRouter history={history}>
|
||||
+ <IonReactRouter>
|
||||
```
|
||||
|
||||
For `IonReactMemoryRouter` (commonly used in tests), use `initialEntries` instead:
|
||||
|
||||
```diff
|
||||
- import { createMemoryHistory } from 'history';
|
||||
- const history = createMemoryHistory({ initialEntries: ['/start'] });
|
||||
- <IonReactMemoryRouter history={history}>
|
||||
+ <IonReactMemoryRouter initialEntries={['/start']}>
|
||||
```
|
||||
|
||||
**IonRedirect Removed**
|
||||
|
||||
The `IonRedirect` component has been removed. Use React Router's `<Navigate>` component instead:
|
||||
|
||||
```diff
|
||||
- import { IonRedirect } from '@ionic/react';
|
||||
- <IonRedirect path="/old" to="/new" exact />
|
||||
+ import { Navigate } from 'react-router-dom';
|
||||
+ <Route path="/old" element={<Navigate to="/new" replace />} />
|
||||
```
|
||||
|
||||
**Path Regex Constraints Removed**
|
||||
|
||||
React Router v6 no longer supports regex constraints in path parameters (e.g., `/:tab(sessions)`). Use literal paths instead:
|
||||
|
||||
```diff
|
||||
- <Route path="/:tab(sessions)" component={SessionsPage} />
|
||||
- <Route path="/:tab(sessions)/:id" component={SessionDetail} />
|
||||
+ <Route path="/sessions" element={<SessionsPage />} />
|
||||
+ <Route path="/sessions/:id" element={<SessionDetail />} />
|
||||
```
|
||||
|
||||
**IonRoute API Changes**
|
||||
|
||||
The `IonRoute` component follows the same API changes as React Router's `<Route>`. The `render` prop has been replaced with `element`, and the `exact` prop has been removed:
|
||||
|
||||
```diff
|
||||
- <IonRoute path="/foo" exact render={(props) => <Foo {...props} />} />
|
||||
+ <IonRoute path="/foo" element={<Foo />} />
|
||||
```
|
||||
|
||||
For more information on migrating from React Router v5 to v6, refer to the [React Router v6 Upgrade Guide](https://reactrouter.com/6.28.0/upgrading/v5).
|
||||
|
||||
@@ -138,7 +138,7 @@ export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildr
|
||||
let leavingLocationInfo: RouteInfo;
|
||||
/**
|
||||
* A programmatic navigation was triggered.
|
||||
* e.g., `<Redirect />`, `history.push()`, or `handleNavigate()`
|
||||
* e.g., `<Navigate />`, `navigate()`, or `handleNavigate()`
|
||||
*/
|
||||
if (incomingRouteParams.current) {
|
||||
/**
|
||||
@@ -176,7 +176,7 @@ export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildr
|
||||
|
||||
/**
|
||||
* A `REPLACE` action can be triggered by React Router's
|
||||
* `<Redirect />` component.
|
||||
* `<Navigate />` component.
|
||||
*/
|
||||
if (action === 'REPLACE') {
|
||||
incomingRouteParams.current = {
|
||||
@@ -280,7 +280,7 @@ export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildr
|
||||
} else {
|
||||
routeInfo.pushedByRoute = lastRoute?.pushedByRoute ?? leavingLocationInfo.pathname;
|
||||
}
|
||||
// Triggered by `history.replace()` or a `<Redirect />` component, etc.
|
||||
// Triggered by `navigate()` with replace or a `<Navigate />` component, etc.
|
||||
} else if (routeInfo.routeAction === 'replace') {
|
||||
/**
|
||||
* Make sure to set the `lastPathname`, etc.. to the current route
|
||||
@@ -566,7 +566,6 @@ export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildr
|
||||
<RouteManagerContext.Provider value={routeMangerContextValue}>
|
||||
<NavManager
|
||||
ionRoute={IonRouteInner}
|
||||
ionRedirect={{}}
|
||||
stackManager={StackManager}
|
||||
routeInfo={routeInfo}
|
||||
onNativeBack={handleNativeBack}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { NavContext } from '../contexts/NavContext';
|
||||
|
||||
export interface IonRedirectProps {
|
||||
path?: string;
|
||||
exact?: boolean;
|
||||
to: string;
|
||||
routerOptions?: unknown;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface IonRedirectState {}
|
||||
|
||||
export class IonRedirect extends React.PureComponent<IonRedirectProps, IonRedirectState> {
|
||||
context!: React.ContextType<typeof NavContext>;
|
||||
|
||||
render() {
|
||||
const IonRedirectInner = this.context.getIonRedirect();
|
||||
|
||||
if (!this.context.hasIonicRouter() || !IonRedirect) {
|
||||
console.error(
|
||||
'You either do not have an Ionic Router package, or your router does not support using <IonRedirect>'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return <IonRedirectInner {...this.props} />;
|
||||
}
|
||||
|
||||
static get contextType() {
|
||||
return NavContext;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ export { IonBackButton } from './navigation/IonBackButton';
|
||||
export { IonRouterOutlet } from './IonRouterOutlet';
|
||||
export { IonIcon } from './IonIcon';
|
||||
export * from './IonRoute';
|
||||
export * from './IonRedirect';
|
||||
|
||||
export * from './IonRouterContext';
|
||||
|
||||
// Utils
|
||||
|
||||
@@ -7,8 +7,6 @@ import type { RouteInfo } from '../models';
|
||||
|
||||
export interface NavContextState {
|
||||
getIonRoute: () => any;
|
||||
getIonRedirect: () => any;
|
||||
getPageManager: () => any;
|
||||
getStackManager: () => any;
|
||||
goBack: (route?: string | RouteInfo, animationBuilder?: AnimationBuilder) => void;
|
||||
navigate: (
|
||||
@@ -27,9 +25,7 @@ export interface NavContextState {
|
||||
}
|
||||
|
||||
export const NavContext = /*@__PURE__*/ React.createContext<NavContextState>({
|
||||
getIonRedirect: () => undefined,
|
||||
getIonRoute: () => undefined,
|
||||
getPageManager: () => undefined,
|
||||
getStackManager: () => undefined,
|
||||
goBack: (route?: string | RouteInfo) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
|
||||
@@ -11,7 +11,6 @@ import type { RouterDirection } from '../models/RouterDirection';
|
||||
import type { RouterOptions } from '../models/RouterOptions';
|
||||
|
||||
import type { LocationHistory } from './LocationHistory';
|
||||
import PageManager from './PageManager';
|
||||
|
||||
// TODO(FW-2959): types
|
||||
|
||||
@@ -30,7 +29,6 @@ interface NavManagerProps {
|
||||
onSetCurrentTab: (tab: string, routeInfo: RouteInfo) => void;
|
||||
onChangeTab: (tab: string, path: string, routeOptions?: any) => void;
|
||||
onResetTab: (tab: string, path: string, routeOptions?: any) => void;
|
||||
ionRedirect: any;
|
||||
ionRoute: any;
|
||||
stackManager: any;
|
||||
locationHistory: LocationHistory;
|
||||
@@ -61,10 +59,8 @@ export class NavManager extends React.PureComponent<NavManagerProps, NavContextS
|
||||
goBack: this.goBack.bind(this),
|
||||
hasIonicRouter: () => true,
|
||||
navigate: this.navigate.bind(this),
|
||||
getIonRedirect: this.getIonRedirect.bind(this),
|
||||
getIonRoute: this.getIonRoute.bind(this),
|
||||
getStackManager: this.getStackManager.bind(this),
|
||||
getPageManager: this.getPageManager.bind(this),
|
||||
routeInfo: this.props.routeInfo,
|
||||
setCurrentTab: this.props.onSetCurrentTab,
|
||||
changeTab: this.props.onChangeTab,
|
||||
@@ -111,14 +107,6 @@ export class NavManager extends React.PureComponent<NavManagerProps, NavContextS
|
||||
this.props.onNavigate(path, action, direction, animationBuilder, options, tab);
|
||||
}
|
||||
|
||||
getPageManager() {
|
||||
return PageManager;
|
||||
}
|
||||
|
||||
getIonRedirect() {
|
||||
return this.props.ionRedirect;
|
||||
}
|
||||
|
||||
getIonRoute() {
|
||||
return this.props.ionRoute;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user