flow icon indicating copy to clipboard operation
flow copied to clipboard

History#replaceState trigger navigation event with React router

Open leluna opened this issue 1 year ago • 1 comments

Description of the bug

When using React router, History#pushState and #replaceState trigger navigation event (trigger HISTORY, coming from a dom event "ui-navigate"). Even if only query parameters are changed.

With Flow Router, no navigation event occurs, not matter which part of the path has changed.

May relate to

  • #19580 Also has something to do with breaking behavior change in History#replaceState with React Router
  • #19540 Also has something to do with undesired navigation event
  • #19494 Also has something to do with undesired HISTORY navigation event if the target URL does not exactly match the current one
  • #19539 This sounds like a similar fix, but only applied to pushState. Indeed, pushState work as Flow Router and also as I would expect.

Expected behavior

No navigation events on History#pushState and #replaceState. If a navigation event is desired, I would use UI.navigate.

Minimal reproducible example

I modified the example from a comment in #19580

@Route(value = "test/:path?")
public class TestView extends VerticalLayout implements BeforeEnterObserver, BeforeLeaveObserver {

    public TestView() {
        add(new Button("replace state - no query params",
                event -> UI.getCurrent().getPage().getHistory().replaceState(null, "test")));
        // no navigation only if the current URL is the same including query params
        add(new Button("replace state - push query param",
                event -> UI.getCurrent().getPage().getHistory().replaceState(null, "test?new")));
        // no navigation only if the current URL is the same including query params
        add(new Button("replace state - change path param",
                event -> UI.getCurrent().getPage().getHistory().replaceState(null, "test/new")));
        // no navigation only if the current URL is the same including query params
        add(new Button("push state - no query params",
                event -> UI.getCurrent().getPage().getHistory().pushState(null, "test")));
        // no navigation
        add(new Button("push state - query param",
                event -> UI.getCurrent().getPage().getHistory().pushState(null, "test?new")));
        // no navigation
        add(new Button("push state - change path param",
                event -> UI.getCurrent().getPage().getHistory().pushState(null, "test/new")));
        // no navigation
    }

    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        add(new Div("beforeEnter"));
    }

    @Override
    public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent) {
        add(new Div("beforeLeave"));
    }
}

Versions

  • Vaadin / Flow version: 24.4.3
  • Java version: 17
  • OS version: Windows
  • Browser version (if applicable):
  • Application Server (if applicable): Spring Boot
  • IDE (if applicable):

leluna avatar Jun 20 '24 08:06 leluna

I can confirm that the following code:

ui.get().getPage().getHistory().replaceState(null, urlWithParameters);

cause the following issue: https://github.com/vaadin/flow/issues/19540

alexanoid avatar Jun 25 '24 19:06 alexanoid

The issue was triaged and currently added to the backlog priority queue for further investigation

mcollovati avatar Jul 11 '24 12:07 mcollovati

@mcollovati Thanks for looking into this. Is there any chance to get this fixed? I'm stuck on the prerelease version because of this issue and unable to upgrade to the stable version :(

alexanoid avatar Jul 17 '24 17:07 alexanoid

We will start looking at this issue soon, but currently I cannot provide any estimate on when the bug will be fixed

mcollovati avatar Jul 18 '24 09:07 mcollovati

This is blocking use from upgrading as well. Is there any update? Thanks

jameskerrtaskize avatar Jul 25 '24 09:07 jameskerrtaskize

@alexanoid what is the latest prerelease version that works for you? Looks like we'll need to downgrade as well then for a while.

jorgheymans avatar Jul 30 '24 10:07 jorgheymans

@alexanoid what is the latest prerelease version that works for you? Looks like we'll need to downgrade as well then for a while.

24.4.0.beta5

alexanoid avatar Jul 30 '24 10:07 alexanoid

I see an issue with the no navigation functionality shown by the example also. So now the route has a path parameter, but as there is no navigation event for the push state in

        add(new Button("push state - change path param",
                event -> UI.getCurrent().getPage().getHistory().pushState(null, "test/new")));
        // no navigation

we actually do not get the new path parameter to the server at all. So the replaceState that sends the data to the server seems like the correct functionality as that gets us the parameter new where as for the pushState at the moment it doesn't and that doesn't seem correct.

for the query parameter change I would also expect to get it to the server, but the question is should all the events be fired (beforeEnter, beforeLeave, afterNavigation, setParameter) or should a navigation to the same target only execute beforeEnter and setParameter.

I feel we should get a comprehensive list of navigation expectations to get the full set working as expected instead of making exceptions here and there for the navigation.

caalador avatar Jul 31 '24 04:07 caalador

As a workaround if for some reason the url update needs to happen "outside" of all handling you can just call

JsonValue state = null;
String pathWithQueryParameters = "test/new";
ui.getPage().executeJs("setTimeout(() => { window.history.replaceState($0, '', $1);  })", state, pathWithQueryParameters);

But note that this may break forward/back navigation.

caalador avatar Jul 31 '24 04:07 caalador

If I want to have all navigation events, I I would just use UI#navigate. Isn’t the whole point of the History class that it only pushes/replaces the state without firing events?

leluna avatar Jul 31 '24 05:07 leluna

Well it is used by the framework to handle url updates for navigation etc. so one one hand yes, but on the other hand just directly running the push and replace functions breaks the react-router and unknowingly allowing that through the api is not that good either.

If only using Flow and no Hilla/react then falling back to vaadin-router for now is a possibility by setting the vaadin.react.enable property to false.

caalador avatar Jul 31 '24 05:07 caalador

I can confirm that in my Flow application getUI().get().getPage().getHistory().replaceState(null, deepLinkingUrl); works fine again after setting vaadin.react.enable property to false.

andersforsell avatar Jul 31 '24 12:07 andersforsell

This ticket/PR has been released with Vaadin 24.4.9.

vaadin-bot avatar Aug 19 '24 11:08 vaadin-bot

This ticket/PR has been released with Vaadin 24.5.0.

vaadin-bot avatar Oct 16 '24 19:10 vaadin-bot