My initial question was
How to add url params without the whole page reloading?
@Ajeet Shah told me in the comments that the page shouldn't reload if the pathname is the same. So I figured the problem lies elsewhere. I could pin point the problem and found out it has to do with code splitting, I could even create a reproducible example here.
The problem is the following: When part of the code is loaded asynchronously and it contains routes, calls to history.push()
with the exact same pathname make the page reload.
example.js
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import AsyncComponent from "./components/AsyncComponent";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about/test">About</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<AsyncComponent />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
AsyncComponent.jsx
import React, { Suspense } from "react";
const Component = React.lazy(() => import("./Routes"));
export const AsyncComponent = (props) => {
return (
<Suspense fallback={<div>...loading</div>}>
<Component />
</Suspense>
);
};
export default AsyncComponent;
Routes.jsx
import React, { useEffect } from "react";
import { Switch, Route } from "react-router-dom";
import { useRouteMatch } from "react-router";
import About from "./About";
export const Routes = (props) => {
let { path } = useRouteMatch();
console.log("rendering Routes");
useEffect(() => {
console.log("mounting Routes");
}, []);
return (
<Switch>
<Route exact path={`${path}/test`}>
<About />
</Route>
</Switch>
);
};
export default Routes;
About.jsx
import React, { useEffect } from "react";
import { useHistory } from "react-router";
import { Link } from "react-router-dom";
export const About = () => {
console.log("rendering About");
useEffect(() => {
console.log("mounting About");
}, []);
const h = useHistory();
return (
<div>
<Link
to={{
pathname: "/about/test",
search: "foo=1&bar=2"
}}
>
Push search params to About
</Link>
<button
onClick={() => {
h.push({
pathname: "/about/test",
search: "foo=1&bar=2"
});
}}
>
Push search params to About
</button>
<h2>About</h2>
</div>
);
};
export default About;
What am I doing wrong? Is there a way to avoid this while keeping code-splitting?