Skip to content

Commit

Permalink
fix: multiple fast navigate calls (#20446) (#20544)
Browse files Browse the repository at this point in the history
* fix: multiple fast navigate calls

Fix issue where a slow connection
and fast `navigate` calls throws
exception due to faulty blocker
state change.

Fixes #20404

* Queue new navigations during ongoing navigation.

* fix blocker nav for basepath

Add test view for manual testing.
Test script doesn't fail on double click.

* Remove test class and selenium dependency.

---------

Co-authored-by: caalador <[email protected]>
Co-authored-by: Mikhail Shabarov <[email protected]>
  • Loading branch information
3 people authored Nov 25, 2024
1 parent fabd607 commit e362efa
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
useBlocker,
useLocation,
useNavigate,
type NavigateOptions,
type NavigateOptions, useHref,
} from "react-router-dom";
import type { AgnosticRouteObject } from '@remix-run/router';
import { createPortal } from "react-dom";
Expand Down Expand Up @@ -273,10 +273,12 @@ function Flow() {
});
const location = useLocation();
const navigated = useRef<boolean>(false);
const blockerHandled = useRef<boolean>(false);
const fromAnchor = useRef<boolean>(false);
const containerRef = useRef<RouterContainer | undefined>(undefined);
const roundTrip = useRef<Promise<void> | undefined>(undefined);
const queuedNavigate = useQueuedNavigate(roundTrip, navigated);
const basename = useHref('/');

// portalsReducer function is used as state outside the Flow component.
const [portals, dispatchPortalAction] = useReducer(portalsReducer, []);
Expand Down Expand Up @@ -361,8 +363,18 @@ function Flow() {

useEffect(() => {
if (blocker.state === 'blocked') {
if(blockerHandled.current) {
// Blocker is handled and the new navigation
// gets queued to be executed after the current handling ends.
const {pathname, state} = blocker.location;
queuedNavigate(pathname.substring(basename.length), true, { state: state, replace: true });
return;
}
blockerHandled.current = true;
let blockingPromise: any;
roundTrip.current = new Promise<void>((resolve,reject) => blockingPromise = {resolve:resolve,reject:reject});
// Release blocker handling after promise is fulfilled
roundTrip.current.then(() => blockerHandled.current = false, () => blockerHandled.current = false);

// Proceed to the blocked location, unless the navigation originates from a click on a link.
// In that case continue with function execution and perform a server round-trip
Expand Down
5 changes: 5 additions & 0 deletions flow-tests/test-react-router/pom-production.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-react</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
6 changes: 6 additions & 0 deletions flow-tests/test-react-router/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-react</artifactId>
<version>${project.version}</version>
</dependency>

</dependencies>

<build>
Expand Down
22 changes: 22 additions & 0 deletions flow-tests/test-react-router/src/main/frontend/NavigateView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {useNavigate} from "react-router-dom";
import {
ReactAdapterElement,
RenderHooks
} from "Frontend/generated/flow/ReactAdapter";

class NavigateView extends ReactAdapterElement {
protected render(hooks: RenderHooks): React.ReactElement | null {
const navigate = useNavigate();

return (
<>
<p id="react">This is a simple view for a React route</p>
<button id="react-navigate" onClick={() => navigate("com.vaadin.flow.RouterView"!)}>
Navigate button
</button>
</>
);
}
}

customElements.define('navigate-view', NavigateView);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2024 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.flow;

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.react.ReactAdapterComponent;
import com.vaadin.flow.router.Route;

/**
* Test view for vaadin/flow#20404 Set network to slow 4G and quickly click on
* button. No console exceptions should be shown.
*/
@Route("com.vaadin.flow.ReactNavigateView")
@Tag("navigate-view")
@JsModule("NavigateView.tsx")
public class ReactNavigateView extends ReactAdapterComponent {

}

0 comments on commit e362efa

Please sign in to comment.