Skip to content

framework7-react: pageComponentLoader may resolve the wrong page after back navigation because it assumes the new page is the last child #4387

@LaggardNC

Description

@LaggardNC
  • framework7 + framework7-react version: 9.0.3
  • "preact" 10.29.0
  • Platform and Target: Framework7 React app (used from a Preact project), reproduced in browser navigation flow and observed in mobile-style router stack as well
  • Live Link or CodeSandbox: not available yet, but the bug is reproducible reliably with the steps below

Describe the bug

In Framework7 React, after navigating to a subpage, going back, and then opening another subpage from the same list page, the browser URL updates to the correct target route, but the wrong page become active.

The target page is mounted in DOM, but it becomes page-previous, while the previous list page stays page-current.

To Reproduce

Steps to reproduce the behavior:

  1. Open a page A with links to subpages
  2. Open subpage B
  3. Go back to the "A" page
  4. Open subpage C

Example real flow in my app:

  1. Go to /about-service/rules
  2. Open /about-service/rules/user-agreement
  3. Tap Back
  4. Open /about-service/rules/account-deletion

Expected behavior

The newly opened subpage B should become page-current, and the parent "Rules" page should become page-previous.

Actual Behavior

The browser URL changes to subpage B correctly, but the active page remains the parent "Rules" page.

The newly opened subpage B is present in DOM, but it receives the class page-previous instead of page-current.

Screenshots

I can attach a video if needed, but the DOM state after the broken transition looks like this:

  • URL: /about-service/rules/account-deletion
  • page-current: parent "Rules" page
  • page-previous: "Account deletion" page

Additional context

I traced this to framework7-react/shared/components-router.js, inside pageComponentLoader.

Current logic resolves the mounted page like this:

const pageEl = el.children[el.children.length - 1];
resolve(pageEl);

This assumes that the newly inserted page is always the last child of the router element.

That assumption breaks after a back() flow.

In my reproduction, after returning from subpage A and then opening subpage B, the children order becomes effectively:

  1. older previous page
  2. newly inserted subpage B
  3. current parent page ("Rules")

So el.children[el.children.length - 1] points to the old current page, not to the newly inserted page.

Because of that, router transition logic receives the wrong pageEl and animates the wrong page into page-current.

I confirmed this by instrumenting pageComponentLoader: when opening subpage B, the resolved page element was the parent "Rules" page, while the actual inserted subpage was a different child node.

Suggested fix

Instead of resolving the page as the last child, detect the newly inserted .page by diffing childrenBefore and childrenAfter.

Something like:

const childrenBefore = Array.from(routerEl.children);

function onDidUpdate(componentRouterData) {
  if (componentRouterData !== viewRouter || resolved) return;

  const childrenAfter = Array.from(routerEl.children);
  if (hasSameChildren(childrenBefore, childrenAfter)) return;

  f7events.off('viewRouterDidUpdate', onDidUpdate);

  const insertedPageEl =
    childrenAfter.find(
      (child) => child.classList.contains('page') && !childrenBefore.includes(child)
    ) || el.children[el.children.length - 1];

  pageData.el = insertedPageEl;
  resolve(insertedPageEl);
  resolved = true;
}

Workaround used locally

As a workaround, I patched pageComponentLoader at runtime and changed only the page element resolution step to select the actually inserted .page instead of the last child.

That workaround fixes the issue completely in the reproduction flow.

Investigation summary

Investigation summary:

1. Initially I suspected route guards / async route resolution.
2. After removing that variable, the issue still reproduced.
3. I narrowed it down to a simple flow:
   Rules -> subpage A -> back -> subpage B
4. I reproduced it in an automated browser flow and inspected DOM classes after each transition.
5. Result:
   - URL was correct
   - router route was correct
   - active DOM page was wrong
6. I instrumented router navigation and page loader behavior.
7. That showed `pageComponentLoader` was resolving the wrong page element after `viewRouterDidUpdate`.
8. Specifically, it resolved the last child of the view element, but after back-navigation the newly inserted page was not always the last child.
9. I patched the loader locally to resolve the actually inserted `.page` node.
10. After that, the issue disappeared in the same reproduction scenario.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions