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

Strange behavior with History -> replaceState(state,location) when react is enabled #19580

Closed
tinostein opened this issue Jun 14, 2024 · 10 comments · Fixed by #20846
Closed

Strange behavior with History -> replaceState(state,location) when react is enabled #19580

tinostein opened this issue Jun 14, 2024 · 10 comments · Fixed by #20846

Comments

@tinostein
Copy link

Description of the bug

Since update to Vaadin 24.4.1 there is a strange behavoir when using replaceState() in History class. If I use the method to update the displayed URL (eg. history.replaceState(null,"http://localhost:8080/?test=test") , it results in opening the invalid url http://localhost:8080/http://localhost:8080/?test=test. If I set react.enabled=false, then it works as expected.

Expected behavior

Browser shows expected URL after history.replaceState call.

Minimal reproducible example

Use any Vaadin 24.4.1 project (eg. skeleton-starter-flow-spring.24).

Call method for updating url parameter, eg:

      Button button = new Button("Test - Add URL Parameter", e -> {
           updateUrlRequestParameter("test",textField.getValue());
       });

with:

public static void updateUrlRequestParameter(String key, String value) {
        Page page = UI.getCurrent().getPage();
        page.fetchCurrentURL(url -> {
            try {
                UriComponentsBuilder builder = UriComponentsBuilder.fromUri(url.toURI());
                UriComponents components = builder.build();
                MultiValueMap<String, String> parameters = components.getQueryParams();
                MultiValueMap<String, String> newParams = new LinkedMultiValueMap<>();
                newParams.putAll(parameters);
                if(newParams.get(key) != null) {
                    newParams.remove(key);
                }
                if(value != null) {
                    newParams.put(key, List.of(value));
                }
                String newUrl = builder.replaceQueryParams(newParams).build().toUriString();
                page.getHistory().replaceState(null, newUrl);
            } catch(Exception exception) {
                //handle exception
            }
        });
    }

Versions

  • Vaadin / Flow version: 24.4.1
  • Java version: 17
  • OS version: Windows 11
@mshabarov
Copy link
Contributor

Looks like a bug in Flow/React Router integration that is new in 24.4. react.enabled=false can be used indeed as a workaround for Flow apps unless this bug gets fixed.

@caalador
Copy link
Contributor

ReplaceState requires the origin to be the same as for the current URL so the usual way is to just give the replaced part e.g. ?test=test instead of http://localhost:8080/?test=test

Also instead of going through the extra roundtrip to the client with page.fetchCurrentURL it would perhaps be clearer to just take the current location and update the queryParameters map (keeping the old ones or just replacing it all), so something like:

        UI ui = UI.getCurrent();
        Location activeViewLocation = ui.getActiveViewLocation();
        QueryParameters queryParameters = activeViewLocation.getQueryParameters()
                .merging("test", "test");
        ui.getPage().getHistory().replaceState(null, new Location(activeViewLocation.getPath(), queryParameters));

The direct call to the browser history.replaceState api removes the origin automatically, but react-router navigation navigates to the full given url.

@mshabarov mshabarov moved this from 🔖 High Priority (P1) to 🆕 Needs triage in Vaadin Flow bugs & maintenance (Vaadin 10+) Jun 17, 2024
@mshabarov mshabarov moved this from 🪵Product backlog to 📥Inbox - needs triage in Vaadin Flow ongoing work (Vaadin 10+) Jun 17, 2024
@stefanbrenner
Copy link

stefanbrenner commented Jun 19, 2024

We have another problem that may be related to this one:

We have a component with a route that implements BeforeLeaveObserver. Within this component we call replaceState on the history.

With the legacy Vaddin router the beforeLeave method is not called on the component whereas it is called with the React router.

@Route(value = "test/:child?")  
public class TestView extends VerticalLayout implements BeforeEnterObserver, BeforeLeaveObserver {  
  
    public TestView() {  
        add(new Button("Replace history state", click -> updatePathWithSelectedChild("child2")));  
    }  
  
    @Override  
    public void beforeEnter(BeforeEnterEvent event) {  
        add(new NativeLabel("beforeEnter Called"));  
    }  
  
    @Override  
    public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent) {  
        add(new NativeLabel("beforeLeave Called"));  
    }  
  
    private void updatePathWithSelectedChild(String child) {  
        var page = UI.getCurrent().getPage();  
        page.fetchCurrentURL(url -> page.getHistory().replaceState(null, buildNewLocation(url, child)));  
    }  
  
    private Location buildNewLocation(URL url, String child) {  
        var path = url.getPath();  
        path = path.substring(path.indexOf("/test"), path.lastIndexOf("/") + 1) + child;  
        return new Location(path);  
    }  
  
}

Yet another difference: With the React router the URL is updated accordingly, with the legacy Vaadin router not!

@mshabarov
Copy link
Contributor

@stefanbrenner thanks for your comment. Could you please make a new ticket for the described case. It would be easier to track these issues separately.

@mshabarov mshabarov moved this from 🆕 Needs triage to 🔎 Investigation in Vaadin Flow bugs & maintenance (Vaadin 10+) Jun 25, 2024
@mshabarov mshabarov moved this from 📥Inbox - needs triage to 🪵Product backlog in Vaadin Flow ongoing work (Vaadin 10+) Jun 25, 2024
@stefanbrenner
Copy link

@stefanbrenner thanks for your comment. Could you please make a new ticket for the described case. It would be easier to track these issues separately.

see #19613

@kcsowrabha
Copy link

kcsowrabha commented Sep 26, 2024

The history.replaceState() invocation is still resulting in invalid url. Issue still exists in the latest vaadin 24.4.12 release

@mshabarov mshabarov moved this from 🪵Product backlog to 🅿️Parking lot in Vaadin Flow ongoing work (Vaadin 10+) Nov 27, 2024
@mshabarov mshabarov moved this from 🅿️Parking lot to 🪵Product backlog in Vaadin Flow ongoing work (Vaadin 10+) Dec 10, 2024
@mstahv
Copy link
Member

mstahv commented Dec 17, 2024

Faced an issue that maybe related. In my project it looks like simple url parameters don't work unless disabling react (router). Didn't yet reduce a case, but this seems very critical to me. My app is bit not trivial though regarding routing, it dynamically registers routes (for reasons...).

Example from a view:

@Override
public void setParameter(BeforeEvent beforeEvent, @OptionalParameter String s) {
    // never getting here
    if (s != null) {

Registering view:

@SpringComponent
@ApplicationScope
public class RouteRegistrationBean {

    @EventListener
    void registerRoutes(ServiceInitEvent evt) {
        RouteConfiguration configuration = RouteConfiguration
                .forApplicationScope();

        Arrays.asList(About.class, EntityEditorView.class, EntityExplorer.class)
                .forEach(view -> {
                    configuration.setAnnotatedRoute(view);
                });
    }

}

This vaadin-maven-plugin configuration is a workaround (well, makes the client bundle smallers so maybe best to keep it on anyways):

                <!-- UrlParameters are broken without this !?!, 24.6.0 -->
                <reactEnable>false</reactEnable>

@caalador
Copy link
Contributor

@mstahv If you could get a simple sample that doesn't work for the url parameters.
I tried with just having a dynamically registered view in our react-router test module and that at least did get a call to setParameter with the param annotated as optional.
For using replaceState in navigation you should define the callback as true e.g. page.getHistory().replaceState(null, "/dynamic/hello", true) so that the client will do a server roundtrip.

@caalador caalador self-assigned this Jan 14, 2025
@mshabarov mshabarov moved this from 🪵Product backlog to ⚒️ In progress in Vaadin Flow ongoing work (Vaadin 10+) Jan 15, 2025
caalador added a commit that referenced this issue Jan 15, 2025
Fix navigating with full url
for instance when adding a
query parameter at the end.

Fixes #19580
@caalador caalador moved this from ⚒️ In progress to 🔎Iteration reviews in Vaadin Flow ongoing work (Vaadin 10+) Jan 15, 2025
caalador added a commit that referenced this issue Jan 17, 2025
Fix navigating with full url
for instance when adding a
query parameter at the end.

Fixes #19580
@github-project-automation github-project-automation bot moved this from 🔎 Investigation to ✅ Closed in Vaadin Flow bugs & maintenance (Vaadin 10+) Jan 17, 2025
@github-project-automation github-project-automation bot moved this from 🔎Iteration reviews to Done in Vaadin Flow ongoing work (Vaadin 10+) Jan 17, 2025
@github-project-automation github-project-automation bot moved this from 🔎Iteration reviews to Done in Vaadin Flow ongoing work (Vaadin 10+) Jan 17, 2025
vaadin-bot pushed a commit that referenced this issue Jan 17, 2025
Fix navigating with full url
for instance when adding a
query parameter at the end.

Fixes #19580
vaadin-bot added a commit that referenced this issue Jan 17, 2025
Fix navigating with full url
for instance when adding a
query parameter at the end.

Fixes #19580

Co-authored-by: caalador <[email protected]>
@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.7.0.alpha5 and is also targeting the upcoming stable 24.7.0 version.

@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 24.6.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment