I define my routes like this:
window.myRoutes = ReactDOM.render((
<ReactRouter.Router history={ReactRouter.browserHistory}>
<ReactRouter.Route path="/" component={Home}>
<ReactRouter.IndexRoute
component={Main}
/>
<ReactRouter.Route
path="/subpath"
component={SubPath}
/>
<ReactRouter.Route
path="/subpath2"
component={SubPath2}
/>
</ReactRouter.Route>
</ReactRouter.Router>
), document.getElementById( "main" ) );
But when I try to access that in another component's componentDidMount(), it's undefined:
componentDidMount() { console.log( window.myRoutes ); }
How do I get all the routes defined?
NOTE: I do not just want the routes in this.props.routes as that's just all the routes leading to the current one
With React Router v2/3, the <Route> components don't actually render anything. They are just converted to an object which the <Router> uses to match against the current location's pathname. If you want to have an object containing all of your routes, it is easy enough to define them as an object.
<Router history={browserHistory}>
<Route path="/" component={Home}>
<IndexRoute component={Main} />
<Route path="/subpath" component={SubPath} />
<Route path="/subpath2" component={SubPath2} />
</Route>
</Router>
is equivalent to
var routes = {
path: '/',
component: Home,
indexRoute: {component: Main}
childRoutes: [
{
path: 'subpath',
component: Subpath
},
{
path: 'subpath2',
component: Subpath2
}
]
}
<Router history={browserHistory} routes={routes} />
Related
I have the following two files
AppRoutes.tsx
import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/messages/*" element={<MessageRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
MessageRoutes.tsx
import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
export default function MessageRoutes() {
return (
<Routes>
<Route element={<ProtectedRoutes />}>
<Route path="/" element={<MessageOverview />} />
<Route path="/new" element={<NewMessage />} />
</Route>
</Routes>
);
}
Because I'm using the path "/messages/*" to capture all paths that start with /messages, my MessageRoutes component takes care of these nested routes. I have a final "*" route in the AppRoutes component to capture any url that the app does not support. But if the path would be "/messages/loremipsum", react router does not catch the NotFound route because everything that starts with "/messages" will be handled with the MessageRoutes component.
Does this mean that in every nested route component I now have to add a final <Route path="\*" element={\<NotFound /\>} /\> again, just to support a final catch all route? I don't like this approach. Is there no absolute final catch all for every route?
Does this mean that in every nested route component I now have to add
a final <Route path="\*" element={<NotFound />} \> again
Yes, absolutely. Each Routes component manages its own "scope" of routes for what it can match. For example if the current URL path is "/messages/loremipsum" the root Routes component matches the "/messages/*" and correctly renders the MessageRoutes component. The MessageRoutes component's Routes component then works on matching on the next path segment. Since there is no "*/loremipsum" route path you need another "catch-all" route to handle this.
The issue is that a Routes component isn't aware of what descendent routes any of its routes may possibly be rendering.
Example:
import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/messages/*" element={<MessageRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";
export default function MessageRoutes() {
return (
<Routes>
<Route element={<ProtectedRoutes />}>
<Route path="/" element={<MessageOverview />} />
<Route path="/new" element={<NewMessage />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
}
If you want to have a single "catch-all" route then you'll need to have a single routes configuration.
Example:
import { Route, Routes } from "react-router-dom";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";
import Home from "../pages/Home";
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route element={<ProtectedRoutes />}>
<Route path="/messages">
<Route index element={<MessageOverview />} />
<Route path="new" element={<NewMessage />} />
</Route>
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
);
}
Now when/if the URL path is "/messages/loremipsum" then this Routes component knows what nested routes it is rendering and can match and can correctly render NotFound.
I have simple web page and i have trouble with routing:
Index.tsx:
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<HomePage />
</React.StrictMode>
);
Homepage:
const HomePage = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Main/>}>
</Route>
<Route path="/login" element={<Login/>}>
</Route>
<Route path="/register" element={<Register/>}>
</Route>
</Routes>
</BrowserRouter>
)
};
export default HomePage;
This works as intended -> if user goes to 'www.example.com' Main component is rendered, if user goes to 'www.example.com/login Login` component is rendered and so on.
However, inside my Main component i also want to use routing:
const Main = () => {
return (
<div className="app">
<HomePageHeader/>
<Routes>
<Route path="/" element={<HomePageFeed/>}>
</Route>
<Route path="/animal" element={<AnimalSelection/>}>
</Route>
<Route path="/animal/:id" element={<Animal/>}>
</Route>
</Routes>
</div>
)
}
export default Main;
Main component has header for every content, however depending on the route i want to display different things, for / some default info, for /animal some menu and list of animals, and for /animal/:id info of specific animal.
However, when , inside HomePageFeed i try to redirect there:
let history = useNavigate(); history("/animal")
I get error: router.ts:11 No routes matched location "/animal"
What is correct way how to handle routing such as this? Is this corretly used routing?
Thanks for help!
My opinion is to have a single routing inside your application. Keep your routing part inside your App.js and share it in all your pages.
If you strictly want it to be in different component. Try wrapping the routes in the Main.tsx inside BrowserRouter might work. The problem might be because <Main /> component is not wrapped anywhere inside the BrowserRouter. Try like below,
const Main = () => {
return (
<div className="app">
<HomePageHeader/>
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePageFeed/>}>
</Route>
<Route path="/animal" element={<AnimalSelection/>}>
</Route>
<Route path="/animal/:id" element={<Animal/>}>
</Route>
</Routes>
</BrowserRouter>
</div>
)
}
export default Main;
I am trying to separate concerns with my Routes, keep my react code more organized. I am currently using react-router-dom v5.
I have an Application Routes component that has 3 children as components
AuthenticatedRoutes
PublicRoutes
Error404Route
Each component renders different routes/components, but only the first component (AuthenticatedRoutes) is being matched.
Application Routes
export const ApplicationRoutes = () => (
<Switch>
<AuthenticatedRoutes />
<PublicRoutes />
<Error404Route />
</Switch>
);
Authenticated Routes
export const AuthenticatedRoutes = () => (
<Switch>
<Route exact path='/dashboard'>
<Dashboard />
</Route>
<Route exact path='/profile'>
<Profile />
</Route>
</Switch>
);
Public Routes
export const PublicRoutes = () => (
<Switch>
<Route exact path='/about'>
<About />
</Route>
<Route exact path='/'>
<Home />
</Route>
</Switch>
);
Error Route
export const Error404Route = () => (
<Switch>
<Route>
<Error404 />
</Route>
</Switch>
);
So, was I was saying only the AuthenticatedRoutes (/dashboard and /profile) are being matched, the public routes and error404 route are not.
I thought that if you used a Switch the route will try to match the location pathname, if not, then the Error404Route will display.
Am I missing something? (sure I am)
Thanks!
Try it like this:
Application Routes
import AuthenticatedRoutes from './AuthenticatedRoutes'
import PublicRoutes from './PublicRoutes'
import Error404Route from './Error404Route'
export const ApplicationRoutes = () => (
<Switch>
{AuthenticatedRoutes}
{PublicRoutes}
{Error404Route}
</Switch>
);
Authenticated Routes
import Dashboard from './Dashboard'
import Profile from './Profile'
export const AuthenticatedRoutes = [
<Route exact path='/dashboard' component={Dashboard}/>,
<Route exact path='/profile' component={Profile}>,
];
Public Routes
import Home from './Home'
import About from './About'
export const PublicRoutes = [
<Route exact path='/' component={Home}/>,
<Route exact path='/about' component={About}>,
];
Error Routes
import Error404 from './Error404'
export const Error404Route = [
<Route exact path='/' component={Error404}/>,
];
I've upgraded the react router to version 4 in my application. But now I'm getting the error
Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
What is wrong with this routing?
import {
Switch,
BrowserRouter as Router,
Route, IndexRoute, Redirect,
browserHistory
} from 'react-router-dom'
render((
<Router history={ browserHistory }>
<Switch>
<Route path='/' component={ Main }>
<IndexRoute component={ Search } />
<Route path='cars/:id' component={ Cars } />
<Route path='vegetables/:id' component={ Vegetables } />
</Route>
<Redirect from='*' to='/' />
</Switch>
</Router>
), document.getElementById('main'))
IndexRoute and browserHistory are not available in the latest version, also Routes do not accept children Routes with v4, Instead, you can specify Routes within the component Itself
import {
Switch,
BrowserRouter as Router,
Route, Redirect
} from 'react-router-dom'
render((
<Router>
<Switch>
<Route exact path='/' component={ Main }/>
<Redirect from='*' to='/' />
</Switch>
</Router>
), document.getElementById('main'))
Then in the Main Component
render() {
const {match} = this.props;
return (
<div>
{/* other things*/}
<Route exact path="/" component={ Search } />
<Route path={`${match.path}cars/:id`} component={ Cars } />
</div>
)
}
Similarly in the cars component
you will have
render() {
const {match} = this.props;
return (
<div>
{/* other things*/}
<Route path={`${match.path}/vegetables/:id`} component={ Vegetables } />
</div>
)
}
Nested routes are not available from version react-router 4.x. Here is a basic example straight from react-router documentation on how to code for nesting route secnarios in v4.x.
Also have a look on this question as well Why can I not nest Route components in react-router 4.x?
Is there a command on the console I can execute at run-time that will tell me all the routes? I have used react-router, but the routes don't all work. In rails you can get a list at runtime.
you can get the routes in an array by using below library
https://github.com/alansouzati/react-router-to-array
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import reactRouterToArray from 'react-router-to-array';
// or var reactRouterToArray = require('react-router-to-array');
console.log(reactRouterToArray(
<Route path="/" component={FakeComponent}>
{/* just to test comments */}
<IndexRoute component={FakeComponent} />
<Route path="about" component={FakeComponent}>
<Route path="home" component={FakeComponent} />
<Route path="/home/:userId" component={FakeComponent} />
</Route>
<Route path="users" component={FakeComponent} />
<Route path="*" component={FakeComponent} />
</Route>)
); //outputs: ['/', '/about', '/about/home', '/users']