Match 2 different routes with different parameters in React Router v4 - reactjs

I have a component that I'd like to match to 2 paths: / and /:value. My route looks like this:
<Route path="/(|:value)" render={(props) => { return <MyComponent/>}}
This above route does match /, but will NOT match /:value. If I hard code the route to:
<Route path="/(|mypath)" render={(props) => { return <MyComponent/>}}
it will match both / and /mypath. How can I get my route to match BOTH / and /any-value-i-put-here?

You just need to specify a Route with conditional parameter like
<Route path="/:value?" render={(props) => { return <MyComponent {...props}/>}}
and it will match / and /any-value. After this you can access the param if it exists like this.props.match.params.value
P.S. Also when you use render, make sure you pass the props to the
rendered component

Related

How can I add an indefinite amount of parameters in react router dom v6? [duplicate]

Trying to create a file renderer but I do not know how to create in a right way. The snippet that I have is that one but is super repetitive:
<Routes>
<Route path='/explorer' element={<Files>}>
<Route path=':root' element={<Files>}>
<Route path=':branch' element={<Files>}>
<Route path=':leaf' element={<Files>} />
...
</Route>
</Route>
</Route>
</Routes>
Examples:
/explorer/home
/explorer/home/username
/explorer/home/username/documents
...
If I use useParams hook from react-router-dom sometimes some params would be undefined and I would like to ignore these params (looping all params I could do it but I do not think is the best practise)
With that params after I create an array and make a request to display all the files or the folders of the selected path (/explorer/home/username)
Is it some way to set a generic number of params for just one component and get a params object with just the need it params?
<Routes>
<Route path='/explorer' element={<Files>}>
<Route path='??' element={<Files>}>
</Route>
<Routes>
Just a suggestion I have for a simple way to manage this would be to render a single route with no path parameters and a trailing wildcard character "*" to continue matching after the first path segment.
Example:
<Routes>
<Route path="/explorer/*" element={<Files />} />
</Routes>
The Files component will then access the entire location.pathname and apply a little string manipulation to get the file/directory structure from the URL path segments.
Example:
const Files = () => {
const { pathname } = useLocation();
const path = pathname
.slice(1) // remove leading "/"
.split("/") // split path directories
.slice(1); // remove leading "explorer" route path
return (
...
);
};
pathname
path array
/explorer/home
["home"]
/explorer/home/username
["home", "username"]
/explorer/home/username/documents
["home", "username", "documents"]
/explorer/home/username/documents/math/exercises
["home", "username", "documents", "math", "exercises"]
What you do with path from here is up to you.
The other solution I'd proposed was to pass the path as a query parameter, i.e. "/explorer?path=/home, and apply a similar path string processing. Perhaps something like the following:
const [searchParams] = useSearchParams();
const stringPath = searchParams.get('path');
const path = stringPath
.slice(1) // remove leading "/"
.split("/"); // split path directories

Why is my custom hook not re-initialised on path change?

I am using ReactRouter to route the application (BrowserRouter at the top level) with a Switch that includes all the routes.
In my use-case I want to be able to handle paths that include the path-parameters (bedId) and navigate between different sub-paths (i.e. /beds/:bedId/, /beds/:bedId/info/) as well asa case where the path is (/beds/-).
I also want to be able to direct user to a different "bed" while they are already on some bed, so /beds/bed1/info -> /beds/bed2, and so on...
My BedView component is responsible for routing within that /beds/:bedId path like so:
// App.jsx (fragment)
<Switch>
<Route
path="/"
exact
render={() => (<Redirect to="/beds/-"/>)}
/>
<Route
path="/beds/-"
exact
component={SomeOtherComponent}
/>
<Route
path="/beds/:bedId"
component={BedView}
/>
</Switch>
The problem occurs when I try to use a hook that relies on the current path-parameter to fetch the latest data (i.e. call to /beds/bed1 will result in a call to http://myapi.com/beds/bed1/timeseries). The useLatestData hook is called from the BedView component, which look like so:
// BedView.jsx (fragment)
export default function BedView(props) {
const {bedId} = props.match.params;
let {path, url} = useRouteMatch('/beds/:bedId');
const bedData = useLatestData({
path: `/beds/${bedId}/timeseries`,
checksumPath: `/checksum/timeseries`,
refresh: false
});```
if(!bedData){
return <Loading/>;
}
return (
<Switch>
<Route exact path={path}>
<Redirect to={`${url}/info`}/>
</Route>
<Route exact path={`${path}/info`} >
<SomeComponent info={bedData.info} />
</Route>
</Switch>
}
...and the useLatestData hook is available here.
The problem is the fact that upon redirecting from /beds/bed1/info to /beds/bed2/info, the hook does not update its props, even though the BedView component seems to be re-rendering. I have created a version of the hook that 'patches' the problem by adding an useEffect hook in my custom hook, to detect the change in path (as supplied in the function arguments) and set data to null, but then the behaviour changes on the BedView.jsx's end - making the following check fail:
if(!bedData){
return <Loading/>;
}
I'm not entirely sure which part is the culprit here and any guidance would be much appreciated! As far as I'm aware, the hook is no re-initialised because the path change still results in the same component. There is also one caveat, once I change the BrowserRouter to include the forceRefresh flag, everything works fine. Naturally, I don't want my page to refresh with every redirect though...
try this:
const {bedId} = props.match.params;
let {path, url} = props.match;

Get url as object in react

How can we get url as object not string.
This is my sample code below.
App.js
return (
<Switch>
<Route path='/:company/:project/:todo' component={Project} />
<Route path='/:company/:project' component={Project} />
<Route path='/:company' component={Projects} />
</Switch>
)
For example when a url is like companyxyz/project0/todo0. Somewhere in any of the components can get
{
company: companyxyz,
project: project0,
todo: todo0
}
Another example is companyxyz/project0. Then it will create like this.
{
company: companyxyz,
project: project0
}
As the examples above match their corresponding Route and map into object as Route is keys and Url is values
I use useLocation() but it returns a pathname with a url in string. I also use useParams() but returns empty object.
It is useParams() that can give us the expected results. The problem was, I use useParams() inside App.js which on this thread Can useParams be able to use inside App.js? can answer it.
For summary, useParams can only give us the object when we use it except in App.js

React Router: Redirecting URLs with a specific param

I'm updating a data repository site where datasets are mapped to an id, which is the param used in our url paths. A few datasets got corrupted recently and part of the solution involved changing their ids. Problem is, a lot of users are linked to datasets on our site - some of which are dead now that those aforementioned ids have changed.
For now, I'm just doing a quick client-side redirect on the 5 or so ids that are dead. I just want to redirect the user from /datasets/oldID to /datasets/newID but I can't find anything in the docs about literally redirecting to a different url. Yep, hardcoding it.
If www.example.com/rootpath/dataset/001 is dead and is now www.example.com/rootpath/dataset/002, how can I redirect the user FROM www.example.com/rootpath/dataset/001 and TO www.example.com/rootpath/dataset/002?
Here's the dataset routes setup
const DatasetRoutes = ({ dataset }) => (
<Switch>
<Route
name="dataset"
exact
path="/datasets/:datasetId"
render={() => <DatasetContent dataset={dataset} />}
/>
<Route
name="download"
exact
path="/datasets/:datasetId/download"
component={DownloadDataset}
/>
<Route
name="publish"
exact
path="/datasets/:datasetId/publish"
component={() => (
<Publish datasetId={dataset.id} metadata={dataset.metadata} />
)}
/>
/* ...
more routes etc
*/ ...
</Switch>
)
I'm kind of baffled that I can't figure out how to do something so presumably simple with React Router v4. I've tried several things...is there a straightforward solution to this?
You can handle the redirect in DatasetContent component:
Set up a dictionary mapping old ids that require redirecting to new ids.
Use the useParams hook (or however you are accessing the params) and in the DatasetContent component:
const map = {
oldId: "newId"
};
let { datatsetId } = useParams();
if (map.hasOwnProperty(datasetId) {
return (<Redirect to={`/datasets/${map[datasetId]}`});
}
// the rest of your original DatasetContent rendering code.
...

React router is not reloading page when url matches the same route

I have following route. When I'm at /createtest page and doing history.push(/createtest/some-test-id) as it matches the same route component, it is not reloaded. Is there any smart solution? Or I need to check match params and implement logic to reload?
(react 16, router v4)
<Route path="/createtest/:testid?/:type?/:step?/:tab?" render={(props) => <CreateTest user={user} {...props}/>}/>
You could give the URL parameters as key to the component so that an entirely new component will be created when a URL parameter changes.
<Route
path="/createtest/:testid?/:type?/:step?/:tab?"
render={props => {
const {
match: {
params: { testid, type, step, tab }
}
} = props;
return (
<CreateTest
key={`testid=${testid}&type=${type}&step=${step}&tab=${tab}`}
user={user}
{...props}
/>
);
}}
/>;
You can try this
const Reloadable = (props) => {
const { location } = props
const [key, setKey] = useState(location.key)
if (location.key !== key) {
setTimeout(() => setKey(location.key), 0)
return null
}
return <>{props.children}</>
}
<Route path="/" exact render={({ history, location }) => (
<Reloadable location={location}>
<SomeComponent />
</Reloadable>)}
/>
I think something like this might help solve your problem
<Route key={'notReloadableRoute'} ... />
(static key, like a regular string)
See this doc.
https://reacttraining.com/react-router/web/api/Route/exact-bool
The exact param comes into play when
you have multiple paths that have similar names:
For example, imagine we had a Users component that displayed a list of
users. We also have a CreateUser component that is used to create
users. The url for CreateUsers should be nested under Users. So our
setup could look something like this:
Now the problem here, when we go to
http://app.com/users the router will go through all of our defined
routes and return the FIRST match it finds. So in this case, it would
find the Users route first and then return it. All good.
But, if we went to http://app.com/users/create, it would again go
through all of our defined routes and return the FIRST match it finds.
React router does partial matching, so /users partially matches
/users/create, so it would incorrectly return the Users route again!
The exact param disables the partial matching for a route and makes
sure that it only returns the route if the path is an EXACT match to
the current url.
So in this case, we should add exact to our Users route so that it
will only match on /users:

Resources