Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: Nested IonRouterOutlet prevents component unmount #20597

Open
OoDeLally opened this issue Feb 24, 2020 · 30 comments
Open

bug: Nested IonRouterOutlet prevents component unmount #20597

OoDeLally opened this issue Feb 24, 2020 · 30 comments
Labels
package: react @ionic/react package

Comments

@OoDeLally
Copy link

Bug Report

Ionic version:

5.0.1

Here is a repo with a minimal code to reproduce the bug:
https://github.com/OoDeLally/ionic-nested-router-outlet-bug

Ionic info:

▶ ionic info 

Ionic:

   Ionic CLI       : 5.4.16 (/home/pascal/.nvm/versions/node/v8.16.0/lib/node_modules/ionic)
   Ionic Framework : @ionic/react 5.0.1

Utility:

   cordova-res                          : not installed
   native-run (update available: 0.3.0) : 0.2.9

System:

   NodeJS : v8.16.0 (/home/pascal/.nvm/versions/node/v8.16.0/bin/node)
   npm    : 6.4.1
   OS     : Linux 4.15


@OoDeLally
Copy link
Author

Hi, any news of this?
I've tried to investigate checking the source code, but it seems beyond my level of understanding.

@emindeniz99
Copy link

emindeniz99 commented Mar 23, 2020

All of these issue links show the same problem that I am having trouble with. I replaced IonRouterOutlet with Switch, but IonTabs requires IonRouterOutlet, and I must delete IonTabs, and the app looks like ugly.
Probably, the problem is caused by IonRouterOutlet.
#20844
#20597
#20298
Duplicate of #20298 #20597 #20844

@und3f1ned
Copy link

Anybody? This is a huge blocker for me

@OoDeLally
Copy link
Author

For me too... So far the workaround we use is to force a complete page reload (window.location = '...').
But this is definitely ugly, as the whole page flickers and we lose the parent state.

@und3f1ned
Copy link

und3f1ned commented Apr 6, 2020

window.location

No, God, please, no :) Try this #20707 (comment)

@OoDeLally
Copy link
Author

OoDeLally commented Apr 7, 2020

window.location

No, God, please, no :) Try this #20707 (comment)

It doesn't work for us.
When using <Switch>, the parent component successfully unmounts, but it also unmounts when it should not (when the path changes but should still keep the parent component mounted), leading to a blank page, without any kind of console error.

Staying with window.location = '...' for now, regarless of how ugly it is.

@und3f1ned
Copy link

window.location

No, God, please, no :) Try this #20707 (comment)

It doesn't work for us.
When using <Switch>, the parent component successfully unmounts, but it also unmounts when it should not (when the path changes but should still keep the parent component mounted), leading to a blank page, without any kind of console error.

Staying with window.location = '...' for now, regarless of how ugly it is.

Did you try Ionic lifecycle methods?
Especially this one
useIonViewDidLeave(() => { console.log('ionViewDidLeave event fired'); });

@OoDeLally
Copy link
Author

Did you try Ionic lifecycle methods?
Especially this one
useIonViewDidLeave(() => { console.log('ionViewDidLeave event fired'); });

I just did. None of the 4 events in https://ionicframework.com/docs/react/lifecycle was fired.

@royvangeel
Copy link

I'm also experiencing the same issue, no lifecyle method will be fired. Tried both class and functional component using the ionic docs.

@elylucas
Copy link
Contributor

Hello all,

I'm looking into this now, hope to have something to report on soon.

@elylucas
Copy link
Contributor

Hi All,

We have a dev release that provides better support for nested IonRouterOutlet components. There is a new prop on IonRouterOutlet that lets the router know it is a nested outlet to better perform the transition.

If your'e outlet is a nested outlet directly rendered by a Route in a parent outlet, then use the ionPage prop on the IonRouterOutlet. Here is an example:

App.tsx:

const App: React.FC = () => (
  <IonRouterOutlet>
    <Route path="/sub1" component={Sub1Outlet}  />
    <Route path="/sub2" component={Sub2Outlet} />
  </IonRouterOutlet>
);

Sub1Outlet.tsx:

const Sub1Outlet: React.FC = () =>  (
    <IonRouterOutlet ionPage>
      <Route path="/sub1" exact={true} 
        render={() => <Redirect to="/sub1/page" />} />
      <Route path="/sub1/page" component={Page1} exact={true} />
    </IonRouterOutlet>
  );

Sub2Outlet.tsx:

const Sub2Outlet: React.FC = () =>  (
    <IonRouterOutlet ionPage>
      <Route path="/sub2" exact={true} 
        render={() => <Redirect to="/sub2/page" />} />
      <Route path="/sub2/page" component={Page2} exact={true} />
    </IonRouterOutlet>
  );

If you can, could you try to install it and let us know if it fixes the issue and if you run into any others? To install it run:

npm i @ionic/react@5.3.0-dev.202006222125.df37029 @ionic/react-router@5.3.0-dev.202006222125.df37029

If all goes good this should be available in the next Ionic release.

Thanks!

@mateuskb
Copy link

mateuskb commented Aug 4, 2020

It didn't unmount the component for me. For now I will stick with routerDirection="back". Although it's not correct, it's working the way I need

@emindeniz99
Copy link

emindeniz99 commented Aug 5, 2020

The issue continues. There is no need for nested ionrouteroutlet. The problem occurs at only one router.
#20298
#20844
#20597

@jasonogasian
Copy link

@elylucas thanks for the fix! I'm noticing that going "back" from nested routes seems to have no transition animation when going back from a "level-3" nested route to a "level-2" nested route.

@basicBrogrammer
Copy link

I first noticed this issue with a single layer of routes (no nesting) and an apollo query with the no-cache option. Desired outcome, anytime i navigate to page X it would re-query the backend. I was surprised to see it not making the query a 2nd or more times. After days of searching git issues and changing versions of apollo/ionic etc i tried a useEffect callback function and noticed it wasn't working which is what apollo's useQuery uses to refetch.

All that to say i think the not unmounting components on forward and none directions is a premature optimization.
What if we made this an option on the Route? or something? This way the dev has to opt into the optimization and isn't surprised by it. [If enough ppl +1s this idea i can start working on a PR]

@patelrikin
Copy link

It didn't unmount the component for me. For now I will stick with routerDirection="back". Although it's not correct, it's working the way I need

What did you apply routerDirection="back" on?

@dellagustin
Copy link

Hello @basicBrogrammer, I started working on a react app, currently it does not deviate much from the default one generated by ionic start with a side menu. It does not have nested routing.
I have pages on different routes and I noticed the components do not unmount when I navigate from one route to the other, which for my use case is not good, as I want to refresh the content that I show when the user selects a view from the menu.

I could work it around by replacing IonRouterOutlet with a Switch, and wrapping it an IonContent to give it an id, but I can not judge what kind of side effects it would have. I think that the existing apis should give the developer control on when the component under a route is persisted or not, once the user leaves the route.

I think this is similar to what you described on #20597 (comment), if I understood it well.

@allanyego
Copy link

Hi All,

We have a dev release that provides better support for nested IonRouterOutlet components. There is a new prop on IonRouterOutlet that lets the router know it is a nested outlet to better perform the transition.

If your'e outlet is a nested outlet directly rendered by a Route in a parent outlet, then use the ionPage prop on the IonRouterOutlet. Here is an example:

App.tsx:

const App: React.FC = () => (
  <IonRouterOutlet>
    <Route path="/sub1" component={Sub1Outlet}  />
    <Route path="/sub2" component={Sub2Outlet} />
  </IonRouterOutlet>
);

Sub1Outlet.tsx:

const Sub1Outlet: React.FC = () =>  (
    <IonRouterOutlet ionPage>
      <Route path="/sub1" exact={true} 
        render={() => <Redirect to="/sub1/page" />} />
      <Route path="/sub1/page" component={Page1} exact={true} />
    </IonRouterOutlet>
  );

Sub2Outlet.tsx:

const Sub2Outlet: React.FC = () =>  (
    <IonRouterOutlet ionPage>
      <Route path="/sub2" exact={true} 
        render={() => <Redirect to="/sub2/page" />} />
      <Route path="/sub2/page" component={Page2} exact={true} />
    </IonRouterOutlet>
  );

If you can, could you try to install it and let us know if it fixes the issue and if you run into any others? To install it run:

npm i @ionic/react@5.3.0-dev.202006222125.df37029 @ionic/react-router@5.3.0-dev.202006222125.df37029

If all goes good this should be available in the next Ionic release.

Thanks!

This seemed like a promising take but it didn't work for me. I assume it's because I have the IonRouterOutlet wrapped in a Suspense component. I have resorted to putting up a 404 page. I feel this is much better than using a Switch which makes the app lose the lifecycle events and caching, and also the routing animations. I hope we will have that Redirect working in IonRouterOutlet soon.

@basicBrogrammer
Copy link

Hello @basicBrogrammer, I started working on a react app, currently it does not deviate much from the default one generated by ionic start with a side menu. It does not have nested routing.
I have pages on different routes and I noticed the components do not unmount when I navigate from one route to the other, which for my use case is not good, as I want to refresh the content that I show when the user selects a view from the menu.

I could work it around by replacing IonRouterOutlet with a Switch, and wrapping it an IonContent to give it an id, but I can not judge what kind of side effects it would have. I think that the existing apis should give the developer control on when the component under a route is persisted or not, once the user leaves the route.

I think this is similar to what you described on #20597 (comment), if I understood it well.

I think if you replace IonRouterOutlet with a switch you won't get the ION transition animations. You can change the direction on the given route to "back" and it will reload .... or at least it did last time i played with it.

@basicBrogrammer
Copy link

routerDirection

I added routerDirection back to any link going to the given route that i wanted to "remount"

@wibed
Copy link

wibed commented Dec 8, 2020

what is the state on this issue?
Nested routes result in breaking third party libraries (replacing ionRouterOutlet with Switch works)
anyways navigation is crucial

@allanyego
Copy link

what is the state on this issue?
Nested routes result in breaking third party libraries (replacing ionRouterOutlet with Switch works)
anyways navigation is crucial

You have to remember that replacing IonRouterOutlet with a Switch will mean converting all your useIonViewDidEnter with useEffects and lose animations.

@emindeniz99
Copy link

emindeniz99 commented Jan 7, 2021

Is there any new news?

@MiguelHernandezCh
Copy link

MiguelHernandezCh commented Jan 24, 2021

I found a workaround.

You can set an id to the <IonPage>. When you click the header button, the app will go back and remove your page from the DOM.

This is to force unmount the page, and because this page is no longer on the DOM it cannot execute its life cycles.

This is my helper function

export const removePageFromDOM = (pageId: string) => {
    const element = document.getElementById(pageId)
    setTimeout(()=>{
        return element && element.remove()
    }, 1000) // Give a little time to properly unmount your previous page before removing the old one
}

This is my page

   return (
        <IonPage id="PageId">
              ...
        </IonPage>
    );

This is my custom header

const Header: React.FC<Props> = ({title}) => {
    const history = useHistory();

    const handleBack = ()=>{
        history.goBack();
        removePageFromDOM("PageId")
    }

    return (
        <IonHeader>
            <IonToolbar>
                    <IonButtons slot="start">
                        <IonButton onClick={handleBack}>
                            <IonIcon slot="icon-only" ios={chevronBack} md={arrowBack}/>
                        </IonButton>
                    </IonButtons>
                <IonTitle>{title}</IonTitle>
            </IonToolbar>
        </IonHeader>
    );
};

Hope this helps

@jamt0
Copy link

jamt0 commented Feb 24, 2021

This is a huge blocker for me.

@ItayElgazar
Copy link

ItayElgazar commented Sep 19, 2021

Any updates?

@ashleydavis
Copy link

It seems to me that Ionic just don't deal with these issues.

@e-kotsiuk
Copy link

In case of Vue framework, adding key attribute to "ion-router-outlet" resolved it for me

<ion-router-outlet id="main-content" :key="route.fullPath" ></ion-router-outlet>

@Mati-Educa
Copy link

Mati-Educa commented Mar 27, 2023

I found a workaround. I'm just using the React router feature to go to another page.

import { useHistory } from 'react-router-dom';
let history = useHistory();

//in your link or button
history.replace('your page');

That code triggers an unmounted event.

I hope this workaround is helpful to you.

@jeffvandyke
Copy link

Stumbled across this issue today, found that links and ion items accept a routerOptions property with unmount: true that seems to take care of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package: react @ionic/react package
Projects
None yet
Development

No branches or pull requests