ionic-framework: bug: `match.params` is always an empty object when using `Redirect` in `IonRouterOutlet`
Prequisites
- I have read the Contributing Guidelines.
- I agree to follow the Code of Conduct.
- I have searched for existing issues that already report this problem, without success.
Ionic Framework Version
- v4.x
- v5.x
- v6.x
Current Behavior
When rendering a Redirect component in IonRouterOutlet, when navigating to other pages that utilise parameters in the URL (for example, /tab1/:id), the match.params object will always be empty. This means that parameters cannot be extracted using useParams or reading the props.match.params object directly.
Expected Behavior
After a Redirect component is rendered and it takes the user to the desired page, I would expect any subsequent page visits to be populated with parameters if they are in the URL.
Steps to Reproduce
To reproduce using the code reproduction repo listed:
- Navigate to localhost:8100 (or the test app) in a new tab. This should load tab 2.
- Click on “Tab 1”. This should take you to
/tab1/TESTING. - The page should read “Tab 1 with param: undefined” when it should actually say “Tab 1 with param: TESTING”.
Code Reproduction URL
https://github.com/Nevvulo/ionic-router-redirect-issue
Ionic Info
Ionic:
Ionic CLI : 6.16.3 (/Users/bswar/.nvm/versions/node/v14.16.0/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/react 5.6.13
Utility:
cordova-res : not installed globally native-run : not installed globally
System:
NodeJS : v14.16.0 (/Users/bswar/.nvm/versions/node/v14.16.0/bin/node) npm : 6.14.11 OS : macOS Big Sur
Additional Information
Related issues: https://github.com/ionic-team/ionic-framework/issues/22230
It’s worth mentioning that the official documentation for v5 states that this is valid for a fallback route: https://ionicframework.com/docs/react/navigation#fallback-route
Here is the diff containing just the changes that introduce the problem: https://github.com/Nevvulo/ionic-router-redirect-issue/commit/dfb52bacad714864a8faf1ae435d456eb0a5b956
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 15
- Comments: 25
After further debugging, we noticed that the workaround of removing
computedMatchcan lead to other unexpected issues. The problem is that the page component which is rendered viaRouterwill be recreated several times as props will be changed whenRoutewill recalculatecomputedProps. As a result, we encountered transition flickering issues and extra requests to API.After deeper investigation, we found another more complex workaround and looks like it covers all the issues. The main idea is to fix
findViewItemByRouteInfomethod fromReactRouterViewStackso it will never return invalid view item. This method is provided via context so we can easily patch it.All you need is just to use
IonRouterOutletPatchedinstead ofIonRouterOutlet. Please note that it does not support any other children types expect ofRouteelement, so you can’t useFragmentinside.Hope the Ionic team can add a quick patch based on this solution 👍
Bump for this one! It’s a rather serious issue that’s been going on for years now, so I’m really hoping it could be prioritized 🤞
If anyone needs a version from @legendar’s solution working with Typescript:
and the
matchRouteutil:This happens not only for
<Redirect>but for any<Route>without thepathparam. The issue is somewhere in Ionic StackManager. Looks like it incorrectly calculates the previous view in some cases.E.g. with the following routes:
If we start from any “not found” URL and then navigate to
/homethe Ionic StackManager will calculateenteringViewItemthe same as was rendered for NotFound component and will replace the entering Route element but will leave all other props as is includingcomputedMatchvalues which are empty. This is caused by the following line: https://github.com/ionic-team/ionic-framework/blob/6d4c52aa5bbafd390056eb57a9151c55f9788c17/packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx#L127 but this behaviour can’t be disabled otherwise routes withoutpathparams will not work.I think this one also related https://github.com/ionic-team/ionic-framework/issues/26524
Switchfrom ReactRouter always recalculatedcomputedMatchthis is why it works as a workaround. But in this case, the component tree will affected and some features from the Ionic Router will break (e.g. transitions and life-circle hooks). Route component from React Router can recalculatematchvalues ifcomputedMatchis not provided. So the better workaround will be to removecomputedMatchcalculations:Any updates on this? 👀
We just ran into this on 7.2.2 in these two scenarios:
/loginto/homepage when user is logged in – after the redirect our params on Home page would come up asundefined.undefined.In both cases a refresh would result in params being loaded properly, so it’s something specific to redirect and/or the use of the default route.
We can confirm that the
<Switch></Switch,hack from this message does solve the problem, but as said above – this impacts transitions between pages.@thomasklemm using
<Switch>instead of<IonRouterOutlet>means there will be no transitions between pages.It’s shocking the Ionic team hasn’t even responded to this bug in the router since it was raised in 2018.
Bump
I found something. The reason why sometimes I get an infinity loop is because of this:
More precisely this:
isEqual(viewItem.initialRouteProps, omit(routeElement.props, "children"))This not always evaluates to
truewhen it should.In my specific case, this infinity loop is only happening for the routes that I have inside a
Suspensethat has afallbackprop, andreact-routersends this prop to myviewItem(ref about this here)This
fallbackprop ishugeandisEqual(viewItem.initialRouteProps, omit(routeElement.props, "children"))sometimes evaluates 2 fallback props to equal, sometimes it doesn’t, and I don’t know exactly when this happens.When this
isEqualfails, I get the infinity loop. You can easily reproduce it by replacing it withfalse.So my questions to @legendar specifically are:
isEqualto evaluate that?lodashare you using?Just ran into this issue in 6.19.1. useIonViewWillLeave and useIonViewDidLeave also stop working. Another workaround for web pages, if you don’t mind a refresh :
<Redirect to={() => {window.location.replace(REF); return <AnyComponent />;} /> // where REF is a valid urlPlease look into this, Ionic Team !
Created a minimal reproduction of this bug including a screen recording, where the
match.paramsare empty if a redirect via a fallback route has happened at the beginning of the session.Wrapping the routes in a
<Switch>component fromreact-routerinside of<IonRouterOutlet>fixed the situation for me, the fallback route is now working, params get parsed properly (Found this solution via this message in Discord)@NickAlvesX This check is the main idea of the workaround =) We compare route props that were used when creating the current viewItem (
initialRouteProps) with current route props (routeElement.props). If they are not equal then ionic calculated the wrong viewItem and we return undefined.childrenwas omitted to prevent an infinite loop as they are always not equal. I thinkrenderandcomponentprops should be omitted as well. We don’t use any other render methods exceptchildrenin our project so this is why this code works for us. But yeah all other non-standard methods (likefallbackin your case) should also be omitted. Alternatively, instead of omitting those props, we can define which props we need to compare as we only need to check route configuration props likepath,isExact,strict,sensitive.I have fixed the infinite loop by omitting the
fallbackprop by doingomit(routeElement.props, ['children', 'fallback']).@legendar
I tried that solution but it causes another issue:
Any update on this?
Also note using
history.replaceinside of a fallback route results in the same behaviorThis works in v4 by the way so it feels like a regression.