Reputation: 1520
I have a codesand box below which show the situation I have:
https://codesandbox.io/embed/sad-nash-x4n69
I have an object named dataset
which can have zero, one or more versions. The component should show the latest version if dataset
has any versions or show a default empty page. Because of this two conditions the component has two routes: /dataset
and /dataset/:version
:
If the dataset is empty I need the url route to /dataset/overview
which should the basic info of the dataset
.
If the dataset is not empty I need to redirect the route to /dataset/${dataset.versions}
which will show the latest version data.
Meanwhile, the DatasetView
Component has three tabs: overview
versions
settings
. I hope the page will automatically redirect to /dataset/overview
or /dataset/:version/overview
depends on the dataset.versions
.
But my code below have a bug: when the dataset has no versions, the route will change to /dataset/overview/overview
but not /dataset/overview
. Hope some one can help me solve this problem.
Upvotes: 0
Views: 371
Reputation: 2382
While @dev_junwen's solution probably works, I'd like to provide a simpler one and look into the reason why your original router didn't work.
In your code, the router has a param version
that could be omitted. Since you didn't specify the param as optional, the app always expect the slug following dataset
to be version
and append another /overview
even when it's not needed.
By setting it as optional at the Router
level, the app should work.
<Route path={["/dataset/:version([0-9]+)?", "/dataset"]}>
In order for the existing routes inside the DatassetComponent
component to work, I've added a regex to the version
param so that it only matches digits.
See a working code sandbox here: https://codesandbox.io/s/epic-beaver-c14kk
Upvotes: 2
Reputation: 3654
At first the question seems easy but it took me quite long to figure out how to do this! This is the only way I can think of right now. If there's some suggestions on how to improve the codes, feel free to submit a change request :)
I cannot guarantee that this is the best way of doing this, but it works for your case.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Route,
Switch,
NavLink,
useRouteMatch,
Redirect
} from "react-router-dom";
import "./styles.css";
const datasetWithVersions = {
id: "dataset-with-versions",
versions: 2,
versionList: [
{
version: 1,
size: 20001
},
{
version: 2,
size: 191232
}
],
name: "a-dataset-with-2-versions"
};
const datasetWithNoVerrsions = {
id: "dataset-with-no-versions",
versions: 0,
name: "an-empty-dataset"
};
function loadDataset() {
return new Promise(resolve => {
setTimeout(() => {
if (Math.random() >= 0.5) {
console.log("loading dataset with versions....");
resolve(datasetWithVersions);
} else {
console.log("loading dataset with no versions....");
resolve(datasetWithNoVerrsions);
}
}, 1000);
});
}
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/dataset" component={DatassetComponent} />
<Redirect to="/dataset" />
</Switch>
</Router>
</div>
);
}
function DatassetComponent() {
let [dataset, setDataset] = useState(null);
const match = useRouteMatch();
useEffect(() => {
loadDataset().then(data => setDataset(data));
}, []);
if (!dataset) return <div>loading...</div>;
const getURL = to => {
if (dataset.versions) {
return `${match.path}/${dataset.versions}/${to}`;
}
return `${match.path}/${to}`;
};
return (
<div>
<div className="navbar">
<NavLink to={getURL("overview")} activeClassName="active">
OverView
</NavLink>
{dataset.versions ? (
<NavLink to={getURL("versions")} activeClassName="active">
Versions
</NavLink>
) : null}
<NavLink to={getURL("settings")} activeClassName="active">
Settings
</NavLink>
</div>
<div className="content">
<Switch>
<Route path={getURL("versions")}>
<div>Versions</div>
</Route>
<Route path={getURL("settings")}>
<div>settings</div>
</Route>
<Route path={getURL("overview")}>
<div>Overview for dataset {dataset.name}</div>
</Route>
<Redirect to={getURL("overview")} />
</Switch>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working code sandbox: https://codesandbox.io/s/react-router-optional-path-demo-p50b4?fontsize=14
Upvotes: 1