I'm attempting to migrate an application from a custom library to react/redux. I'm investigating using react-router as our top-level routing tool.
I'm planning on using hashHistory as our existing route tree is done entirely in hashes. However, our existing route tree does not include a leading slash after the #.
In order to not break all our existing links, I'm wondering if there is a way to omit the leading slash.
So far, I've attempted:
import {createHashHistory} from 'history'
const custom_history = useRouterHistory(createHashHistory)({
hashType: 'noslash'
})
class Router extends Component {
render() {
return (
<Router history={custom_history}>
<Route path="" component={Container}>
<Route path="dashboard" component={SubContainer} />
</Route>
</Router>
)
}
}
But using createHashHistory directly from the history library appears to cause an error in the second nested getCurrentLocation call of history.listen.
Is there a means to do this in react-router ^3.0.0?
Related
I have an asp.net MVC application that serves the React app under the following url: http://domain.fake/controller/action. Here is the routing of the react app:
<Routes>
<Route path="/*" element={<Config />}>
<Route path="values" element={<ConfigKeyValues />} />
</Route>
</Routes>
When I try to navigate to the values route using the useNavigate hook:
const nav = useNavigate();
nav("values");
In the URL, instead of adding the /values path, the routing removes the whole controller/action path and only sets the /values path. So instead of getting the url http://domain.fake/controller/action/values, I'm getting http://domain.fake/values, and this is not right.
I'm able to display the component correctly, and I'm not being redirected by the server, but the url is wrong because I can't share that since it does not exist.
How can I prevent the React routing from replacing the base path but adding the new path to the url and displaying the component of that route?
To tell React Router Dom that your application is served from a subfolder, you could use the basename property, which works with createBrowserRouter as well as BrowserRouter:
The basename of the app for situations where you can't deploy to the root of the domain, but a subdirectory.
<BrowserRouter basename="/controller/action">
{/* The rest of your app goes here */}
</BrowserRouter>
createBrowserRouter(routes, {
basename: "/controller/action",
});
Side note, be aware that calling navigate("values"), without / at the beginning of "values" would take into account the path you are on and build on top of it as the doc says.
true navigate method inside react-router-dom will clear the sub path so we need to add it manually
but this is will be bad if you hard coded it
instead we will add it automatically
function YourPage() {
const nav = useNavigate()
const location = useLocation()
return (
<div>
<button onClick={() => nav(`${location.pathname}/yourTargetPath`)}>
Navigate
</button>
</div>
)
}
we will save our sub-path inside the variable and add it to navigate method
this should solve your problem
Prior to react-router 6.4, I was happily declaring my routes using the <Routes> and <Route> components.
Wherever I wanted some route-dependent rendering, I could use these 2 components.
I was able to nest routes, again by using the <Routes> and <Route> components in a nested component. I was also able to even use multiple Routes containers next to each other in 1 component.
I like this a lot because it keeps things small, nested routes can be handled in the nested component and don't need to bloat the root component. But most of all I like it because seeing the routes in your code where they actually will be rendered makes the code very readable and easy to visualize for me.
Now with react-router 6.4 it seems like they are moving more to a configuration based style of routing, where you define all routes on a root level. In some interviews it's clear that the maintainers are proud that they can now define nested routes on a root level. https://reactrouter.com/en/main/start/overview#nested-routes
With that approach, you need to use the <Outlet/> component in spots where you want to render the nested route. When reading the code you to cross reference those Outlet locations with the configuration on the root level to figure out what is rendered where, which makes things much harder to visualize mentally.
So here's my question: what are the advantages of using such a configuration approach. Any guesses to why the maintainers are taking this road?
Clarifications
First, since the introduction of react-router#6 you could use a routes configuration via the useRoutes hook or just use the RRD components and define the JSX for them. This is the "configuration based" vs "component based" routing you describe. It's nothing new in RRDv6.4.
Configuration Example:
import { useRoutes } from 'react-router-dom';
const routes = [
{
element: <Layout />,
children: [
{
path: "/foobar",
element: <FooBar />
},
{
path: "/foo",
element: <Foo />
},
{
path: "/bar",
element: <Bar />
},
],
},
{
path: "/",
element: <Home />
},
];
const App = () => {
const appRoutes = useRoutes(routes);
return appRoutes;
};
Component Example:
import { Routes, Route } from 'react-router-dom';
const App = () => (
<Routes>
<Route element={<Layout />}>
<Route path="/foobar" element={<Foobar />} />
<Route path="/foo" element={<Foo />} />
<Route path="/bar" element={<Bar />} />
<Route>
<Route path="/" element={<Home />} />
</Routes>
);
I believe somewhere in the older RRD docs outright stated that the Routes component was implemented using the useRoutes hook under-the-hood by converting the JSX you pass as children into a routes configuration object.
Second, what you are describing as "nested routes" are actually called descendent routes. Nested routes on the other hand are Route components directly rendering other Route components that render their content into the Outlet of their parent route.
Descendent routes are good for code/route splitting.
Question
So here's my question: what are the advantages of using such a
configuration approach. Any guesses to why the maintainers are taking
this road?
What RRDv6.4 introduced was a new Data API which uses new Data Routers. See Picking a Router. Along with the new Data Routers came an updated Route component with a bunch of new props/functionality to be used within these new Data Routers. Now herein lies the rub. In order for the Route components to take advantage of the new Data APIs (action, errorElement, loader, and shouldRevalidate props) they necessarily need to be defined/configured when creating the Data Router. It doesn't matter if you use a routes "configuration" object with createBrowserRouter or use JSX with both createBrowserRouter and the createRoutesFromElements utility, all the routes that need the Data APIs need to be declared here.
The "advantage" is that you can use the new Data APIs with these routes. Of course the library maintainers are going to advertise and highlight their newest products and features.
AFAIK, the Data APIs don't currently work with descendent routes because these can be dynamic and there's not an easy way for a Route component to know what descendent Route components might be rendered under it in its sub-ReactTree at run-time. This is why nested routes are paramount; it's trivial to setup and maintain the overhead of the Data APIs when you know ahead of time exactly what routes can be rendered and what data they may manage.
Greeting,
I am make a website to display different products.
Assume I have the following routes:
perfume/a-specific-perfume
base/a-specific-base
These routes both display a single page with a single product, which is nice, but what if I have many different type of product. This would lead me to have the following routes:
perfume/a-specific-perfume
base/a-specific-base
water/...
.../...
It leads to repetitive works for each type of product.
Is there a way to ignore the root route? I tried using a wild card, "*/a-specific-perfume" or "*/p/a-specific-perfume", and it did not work. Any suggestion?
In react-router-dom#6 the wildcard character "*" is only valid at the end of a path and only serves to indicate that a route component can render descendent routes. You can use route path params for dynamic path segments.
Examples:
<Route path="/:type/a-specific-perfume" element={....} />
<Route path="/:type/a-specific-base" element={....} />
<Route path="/foo/:type/a-specific-perfume" element={....} />
<Route path="/bar/:type/a-specific-base" element={....} />
In each component (the element prop) use the useParams hook to access the route param value.
"/perfume/a-specific-perfume"
"/base/a-specific-base"
"/water/..."
"/.../..."
import { useParams } from 'react-router-dom';
...
const { type } = useParams(); // "perfume"|"base"|"water"|"..."|etc...
I have installed "react-router-dom": "^6.0.0-beta.0" and created some page routes. Facing problems where the Page404 / NotFound component is not working if a page doesn't exist. For some reason when I am using dynamic page/post slug the component NotFound will not work if there is no page/post with this ID.
Is there an option inside React Router which solves this issue?
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import AllPosts from "components/AllPosts";
import SinglePost from "components/SinglePost";
import NotFound from "components/Page404";
const App = () => (
<Router>
<Routes>
<Route element={<AllPosts />} path="/" exact />
<Route element={<SinglePost />} path="/:slug" />
<Route element={<NotFound />} path="*" />
</Routes>
</Router>
);
export default App;
The Routes and Route component can't possibly know ahead of time that some dynamic path, "/someUnsupportedIdValue" for example, isn't a valid path to be handled by SinglePost.
The options you have here are:
Conditionally render some alternative UI in SinglePost, something along the lines of "A page by this ID isn't found".
Check if a page with matching ID exists, conditionally render that page, or a redirect (Navigate component replaced Redirect in v6) to your more generic 404 page (actually, a redirect to any "throw away" path that isn't explicitly handled already will land you on your 404 page). Or you can imperatively redirect with a navigate(to, { replace: true }).
Try to remove exact because was removed from v6 and make sure Page404 is the correct component or create Notfound.jsx
Check if the post not exists then redirect to Notfound page.
I'm migrating a react application and I'm trying to split it. Basically, I would like to redirect some client-side react routes to absolute urls (or relative, but at least go with a server roundtrip, where reverse proxying is done)
Note that
react-router 3.0.0
react-router-redux 4.0.7
The app have these urls
http://myhost/ => homepage
http://myhost/someroute1 => a first route
http://myhost/someroute2 => a second route
http://myhost/someroute3 => a third route
Everything is inside react right now.
Routing looks like this :
<Provider store={store}>
<Router history={history}>
<Route path="/" component={Root}>
<IndexRoute component={Home} />
<Route path="/someroute1" component={Route1} />
<Route path="/someroute2" component={Route2} />
<Route path="/someroute3" component={Route3} />
</Route>
</Router>
</Provider>
The goal is to redirect, say, routes "/" and "/someroute2" to static urls (server urls). As so :
http://myhost/ => http://anotherhost/
http://myhost/someroute1 => keep react routing
http://myhost/someroute2 => http://anotherhost/anotherroute5
http://myhost/someroute3 => keep react routing
The question is simple : is is possible to replace, in a clean way, a react router route with an absolute url ?
I heard about Redirect and IndexRedirect components, but I can't figure how to use it properly, and, due to a lack of react / react-router, I can't figure if there would be any dangerous side-effects (in history for example).
Use Route's render prop instead of component. That way, you can specify a function to be called instead of a component to be instantiated. In the function, perform the navigation the old-fashioned way, using window.location.href:
<Route
path="/someroute2"
render={() => {
window.location.href = "http://anotherhost/anotherroute5";
return null;
}}
/>
Partially based on #brub answer, I've found a solution using a dumb component.
import React, { Component } from 'react'
export default class MyRedirectRoute extends Component {
componentDidMount() {
window.location.href = //my url here
}
render() {
return null
}
}
That I pass like this
<Route path="/someroute3" component={MyRedirectRoute} />
Though, I'm still not aware of a few things :
Is this a recommended solution ?
Are there any history side-effect ?
Is there any better (more react-router) solution than window.location.href ? I tried this.context.history without any success...
I'm waiting for feedback / better solution before accepting my own answer
You probably don't need React Router for this. The creator of React Router suggests using the <a> tag.
I haven't tried it but syntactically you could do it like this:
<Route
path="/someroute2"
render={() => <Redirect to="http://anotherhost/anotherroute5" />}
/>