Skip to content

Ionic 4 - StackController Page reuse uses old Page data #16516

Closed
@KevinKelchen

Description

@KevinKelchen

Bug Report

Ionic Info

Ionic:

   ionic (Ionic CLI)             : 4.5.0 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.0.0-beta.19
   @angular-devkit/build-angular : 0.10.7
   @angular-devkit/schematics    : 7.0.7
   @angular/cli                  : 7.0.7
   @ionic/angular-toolkit        : 1.2.0

System:

   NodeJS : v10.13.0 (/usr/local/bin/node)
   npm    : 6.4.1
   OS     : macOS Mojave

Describe the Bug
(There are arguably two interrelated but separate issues here. If I should create separate issues then please let me know! 🙂)

Forward navigation to a Page with the same URL (excluding query parameters) will modify the navigation stack history (not the browser history stack but the StackController stack, which are different). It will then re-use a previous Page instance that does not contain the latest navigation data from the forward navigation to the same URL.

Steps to Reproduce
Steps to reproduce the behavior:

  1. Clone this repo on your machine: https://github.com/KevinKelchen/ionic4-stack-page-reuse . It is a slightly modified version of the sidemenu Angular stater template.
  2. In the terminal, path into the repo root and run npm i.
  3. Run ionic serve.
  4. Click the Menu icon and then List.
  5. Click Item 1 to forward navigate to the detail screen for Item 1.
    • You will notice there's a timestamp that displays. I chose a timestamp in this example so that there's an easy way to see on-screen what navigation data is being used.
    • This timestamp data was stored in a shared service prior to navigating from the source Page. It was then retrieved from the target Page from the same service. This pattern was outlined by Josh Morony here. The data in the service is keyed in a dictionary using the navId UUID URL query parameter.
    • A shared service is being used because we are migrating from Ionic 3 and the use of NavParams which allowed passing complex data like callback functions and objects which cannot be serialized in a lossless way in the URL or @ionic/storage.
    • A UUID was chosen so that navigating to what's otherwise the same URL will use the navigation parameters specific to the current navigation. This will also preserve previous navigation parameters so they can be used when revisiting a Page in the browser history.
  6. Click the pencil/create button in the header. This will navigate you to a different Page type called List Detail More.
  7. From this Page, click Item 1 to forward navigate to the detail screen for Item 1. Note that the timestamp is the old value that was displayed when you initially navigated to Item 1. This is because the StackController rolled back the stack and re-used the old instance of the Page for Item 1. The new navId URL query parameter was not used to retrieve the data from the shared service and thus is not using fresh data on this forward navigation.
  8. To observe the StackController navigation stack modification, click the ion-back-button and it will take you to the initial List Page instead of List Detail More which is the screen you were previously on before revisiting Item 1.

Related Code
The steps to reproduce above link to this repository which is set up to reproduce the issue.

Expected Behavior
I expected forward navigation to a Page with the same URL to use the latest, current navigation data from the forward navigation to the same URL and not modify the StackController navigation stack history.

Additional Context
Here is some additional context from a message I posted in #16367:


Note: My definition of "reusing" a Page is when "forward" navigating to a URL and instead of pushing a new Page onto the StackController's "stack" it re-uses an instance of the Page that is already present in the stack.

I've been having issues with StackController as well but on Beta 15. I am using the Angular router imperatively in TypeScript but I believe I was experiencing them as well using the new NavController (which uses the Angular router).

Here's a related behavior I've observed:

When you use the router to "forward" navigate to a Page, if the URL (excluding query parameters) is the same as a Page that is already in the "stack," the StackController will remove all of the previous Pages in the stack back to the first occurrence of the matching URL/Page. It then re-uses that first occurrence of the Page in the stack. And so then when you use ion-back-button, which seems to ignore browser history and only uses the stack, the Page you go back to is not what you would expect--it's the Page prior to the first occurrence of the Page in the stack.

  • Note: Since the Android and browser back button seems to ignore the StackController's "stack," I've tried working around this issue to some success by creating my own custom back button component that just uses Angular's Location.back() when you click it. This tends to result in a lot of destroyed/re-initializing of Pages whose state you can attempt to preserve by storing off data in a shared service that is keyed with a unique ID (such as a UUID) present in the URL.

How StackController re-uses Pages has actually been tripping me up in another way:

If I use the Angular router to "forward" navigate to a Page with the same URL as one in the StackController's stack and I have navigation parameters that I store in a shared service (as opposed to the URL) and key the parameter storage in the service off of a URL query parameter such as a UUID (a query parameter--not using the matrix URL notation as noted at the end of this section), because the Page is being re-used due to a matching URL instead of being considered as different, the new query parameter value is not being used and the Page pulls the previous parameters out of the shared service due to using the old query parameter value.

Perhaps the solve is to work around it by just putting the UUID in as a matrix URL notation parameter and effectively opt-out of any StackController reuse of Pages.

  • Upsides
    • "Forward" navigating to the same Page/URL would use the correct parameters.
    • With all of the Pages in the stack remaining wired up (never destroyed or reused), when you navigate back to them they are exactly the way you left them when you navigated away, which is how Ionic 3 worked.
  • Downsides
    • It's non-default behavior that differs from how Ionic seems to think it should work in Ionic 4.
    • Although it's like Ionic 3, you lose an optimization in which all Pages stay wired-up in the DOM as you continue to forward navigate through the app which may potentially increase the amount of memory that's used by the app; I would have concerns of how this might scale as users keep forward navigating through the app.

Thank you for all that you do and for making a great framework! 🙂

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions