I wonder if there's some way with React Router to get a list of all possible paths with the routes configuration?
For example, from this:
var routes = (
<Route name="/">
<DefaultRoute .../>
<Route name="about" .../>
<Route name="blog" .../>
</Route>
);
I'd like:
["/", "/about", "/blog"]
There are a couple of ways to do this:
The simplest: See your routes as an array by checking your props (this.props.routes). However, this includes a lot of extra data you aren't necessarily looking for.
More robust: Use a community-built tool like react-router-query, react-router-to-array, or react-router-sitemap. All have fairly robust read-me's you can dig into.
Someone asked a similar question on this thread with more detailed responses.
This seems to work for me, with v0.13:
var router = Router.create(routes);
var routePaths = [];
for (var i in router.namedRoutes) {
routePaths.push(router.namedRoutes[i].path);
}
//routePaths = ['/', '/about', '/blog']
I have written a function for this purpose:
/**
* Get list of paths
* #param routes {JSX.Element}
*/
const getRoutes = (routes) =>
{
const paths = new Set();
/**
* Walk in list of routes tree
* #param element {JSX.Element}
*/
const walkTree = (element) =>
{
if (element.props.children && element.props.children.length > 0)
{
element.props.children.forEach((elem) => walkTree(elem));
}
else if (element.props.path && typeof element.props.path === 'string')
{
paths.add(element.props.path);
}
}
walkTree(routes);
return paths;
};
A complete react sample (Tested with react 6):
import './App.scss';
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import Login from './pages/login.page';
import Pricing from './pages/pricing.page';
import Register from './pages/register.page';
import Home from './pages/home.page';
import Dashboard from './pages/dashboard.page';
import Authenticator from './components/Authenticator.component';
import Profile from './pages/Profile.page';
const App = () => {
const routes = (
<Routes>
<Route exact path='/login' element={<Login />} />
<Route exact path='/register' element={<Register />} />
<Route exact path='/pricing' element={<Pricing />} />
<Route path='/' element={<Authenticator />}/>
<Route exact path='/dashboard' element={<Dashboard />} />
<Route exact path='/profile' element={<Profile />} />
</Route>
<Route path='/*' element={<Home />} />
</Routes>
);
/**
* Get list of paths
* #param routes {JSX.Element}
*/
const getRoutes = (routes) =>
{
const paths = new Set();
/**
* Walk in list of routes tree
* #param element {JSX.Element}
*/
const walkTree = (element) =>
{
if (element.props.children && element.props.children.length > 0)
{
element.props.children.forEach((elem) => walkTree(elem));
}
else if (element.props.path && typeof element.props.path === 'string')
{
paths.add(element.props.path);
}
}
walkTree(routes);
return paths;
};
console.log(getRoutes(routes));
return (
<div className="App">
<main id='main' className="main">
{ routes }
</main>
</div>
);
}
export default App;
Related
I've looked online and most questions uses a different version of react-router-dom than what I'm using making the answer hard to find. What I want to do is simple, let's say a user is logged in then I wouldn't want that user to access the "sign-up" page and redirect them to the "home" page.
Here's the code I'm using that isn't working.
import { useAuth } from '../contexts/AuthContext';
import "firebase/auth";
import {Route, Navigate} from 'react-router-dom'
function AuthRoute ({element: Element, ...rest}) {
const { currentUser } = useAuth()
return (
<Route
{...rest}
render={props => {
return !currentUser ? <Element {...props} /> : <Navigate to="/" />
}}
></Route>
)
}
export default AuthRoute;
Here's how it's being called in App.js
return (
<Router>
<div className = "App">
<AuthProvider>
<Routes>
<Route path="/" element={<Home/>}/>
<AuthRoute exact path = "/sign_up" element= {SignUp} />
<Route exact path="/about" element={<About/>}/>
<Route exact path="/login" element={<SignIn/>}/>
</Routes>
</AuthProvider>
</div>
</Router>
);
It routes to sign_up but it doesn't matter if the user exists or not.
I don't know if you still need help with this but I found how to do it,
first your home route should be an exact path, then in AuthRoute() component you can do this:
export default function AuthRoute ({chidren}) {
const { currentUser } = useAuth()
if (currentUser){
return (children)
}else{
return <Navigate to='/sign_up' replace/>
}
}
then in App.js:
<AuthRoute exact path = "/sign_up" element= {SignUp} /> // should be :
<Route path='/sign_up' element={<AuthRoute> <Home/> </AuthRoute>}
</Route>
hope this can help anyone struggling with react-router v6 (like I did)
Basically, I'm working on the DataTurks project but I've stumbled upon some problems linked to the frontend app named bazaar. I'm trying to create a new component and add a new route for it so I can access it. This is my code for my routes:
import React from 'react';
import {IndexRoute, Route} from 'react-router';
// import { isLoaded as isAuthLoaded, load as loadAuth } from 'redux/modules/auth';
import {
App,
Home,
NotFound,
TaggerLogin,
TaggerSpace,
TaggerCreate,
TaggerImport,
TaggerStats,
TaggerExport,
TaggerProjects,
TaggerAdd,
TaggerOveriew,
TaggerVisualize,
TaggerEdit,
TaggerOrg,
TaggerOrgProject,
TaggerError,
TaggerKeyBind,
TaggerContributors,
ConfirmMail
} from 'containers';
export default (store) => {
const requireLogin = (nextState, replace, cb) => {
const { auth: { user }} = store.getState();
if (!user) {
// oops, not logged in, so can't be here!
replace('/projects/login');
}
cb();
};
/**
* Please keep routes in alphabetical order
*/
return (
<Route path="/" component={App}>
<Route path="confirm" component={ConfirmMail}/> //THIS IS THE COMPONENT I'M TRYING TO ACCESS
{ /* Home (main) route */ }
<IndexRoute component={Home}/>
{ /* Routes requiring login */ }
<Route onEnter={requireLogin}>
<Route path="projects/create" component={TaggerCreate}/>
<Route path="projects/edit" component={TaggerEdit}/>
<Route path="projects/:orgName/create" component={TaggerCreate}/>
<Route path="projects/:orgName/import" component={TaggerImport}/>
<Route path="projects/:orgName/:projectName/edit" component={TaggerEdit}/>
<Route path="projects/:orgName/:projectName/keybind" component={TaggerKeyBind}/>
</Route>
{ /* Dataturks tool */}
<Route path="projects/login" component={TaggerLogin}/>
<Route path="projects/import" component={TaggerImport}/>
<Route path="projects/space" component={TaggerSpace}/>
<Route path="projects/stats" component={TaggerStats}/>
<Route path="projects/export" component={TaggerExport}/>
<Route path="projects" component={TaggerProjects}/>
<Route path="projects/add" component={TaggerAdd}/>
<Route path="projects/overview" component={TaggerOveriew}/>
<Route path="projects/visualize" component={TaggerVisualize}/>
<Route path="projects/errors" component={TaggerError}/>
<Route path="projects/:orgName" component={TaggerOrg} />
<Route path="projects/:orgName/:projectName" component={TaggerOrgProject} />
<Route path="projects/:orgName/:projectName/space" component={TaggerSpace}/>
<Route path="projects/:orgName/:projectName/export" component={TaggerExport}/>
<Route path="projects/:orgName/:projectName/overview" component={TaggerOveriew}/>
<Route path="projects/:orgName/:projectName/visualize" component={TaggerVisualize}/>
<Route path="projects/:orgName/:projectName/contributors" component={TaggerContributors}/>
{ /* Catch all route */ }
<Route path="*" component={NotFound} status={404} />
</Route>
);
};
What I'm trying to access is ConfirmMail component and route. The ConfirmMail component:
import React from 'react';
export default function ConfirmMail() {
const styles = require('./ConfirmMail.scss');
return (
<div className="container">
<div className={styles.loading + ' text-center'}>
<span className="glyphicon glyphicon-repeat glyphicon-refresh-animate gi-3x">
CONFIRMED
</span>
</div>
</div>
);
}
Also, the client.js:
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import createStore from './redux/create';
import ApiClient from './helpers/ApiClient';
import {Provider} from 'react-redux';
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { ReduxAsyncConnect } from 'redux-async-connect';
import useScroll from 'scroll-behavior/lib/useStandardScroll';
import {persistStore} from 'redux-persist';
import getRoutes from './routes';
import { Segment } from 'semantic-ui-react';
const client = new ApiClient();
const _browserHistory = useScroll(() => browserHistory)();
console.log('window data is', window._data);
const store = createStore(_browserHistory, client, window.__data);
const history = syncHistoryWithStore(_browserHistory, store);
class AppProvider extends React.Component {
constructor() {
super();
this.state = { rehydrated: false };
}
componentWillMount() {
persistStore(store, { blacklist: ['routing', 'dataturksReducer'] }, () => {
this.setState({ rehydrated: true });
});
}
render() {
const component = (
<Router render={(props) =>
<ReduxAsyncConnect {...props} helpers={{client}} filter={item => !item.deferred} />
} history={history}>
{getRoutes(store)}
</Router>
);
if (!this.state.rehydrated) {
return (<div><Segment basic loading/></div>);
} else if (this.state.rehydrated) {
console.log('rehydrating ', component);
return (
<Provider store={store} key="provider">
{component}
</Provider>);
}
}
}
const dest = document.getElementById('content');
ReactDOM.render(
<AppProvider />,
dest
);
// const persistor = persistStore(store, {}, () => { this.rehydrated = true; });
This is the main App.js component in this project.
So when I access localhost/confirm, I don't get a 404 not found page but I get an empty page. Earlier I've tried to console.log a few things but it didn't work either. I've been working on this my whole day and I didn't manage to make it work. Does someone know what am I doing wrong?
Did you try to add / in Route path?
<Route path="/confirm"
You need to add / before every route. That's why it is not getting the component you want. So modify your code by adding / like the following
<Route path="/confirm" component={ConfirmMail}/>
And another thing that I recommend you to do is enclose your routers inside <Switch></Sitch>. If the URL is /confirm, then all routes below it will be rendered. This is by design, allowing us to compose s into our apps in many ways, like sidebars and breadcrumbs, bootstrap tabs, etc.
Occasionally, however, we want to pick only one to render. If we’re at /confirm we don’t want to also match /:orgName If so you will end up with 404 error.
Here’s how to do it with Switch:
return (
<Switch>
<Route path="/" component={App}/>
<Route path="confirm" component={ConfirmMail}/> //THIS IS THE COMPONENT I'M TRYING TO ACCESS
{ /* Home (main) route */ }
<IndexRoute component={Home}/>
{ /* Routes requiring login */ }
<Route onEnter={requireLogin}>
<Route path="projects/create" component={TaggerCreate}/>
<Route path="projects/edit" component={TaggerEdit}/>
<Route path="projects/:orgName/create" component={TaggerCreate}/>
<Route path="projects/:orgName/import" component={TaggerImport}/>
<Route path="projects/:orgName/:projectName/edit" component={TaggerEdit}/>
<Route path="projects/:orgName/:projectName/keybind" component={TaggerKeyBind}/>
</Route>
{ /* Dataturks tool */}
<Route path="projects/login" component={TaggerLogin}/>
<Route path="projects/import" component={TaggerImport}/>
<Route path="projects/space" component={TaggerSpace}/>
<Route path="projects/stats" component={TaggerStats}/>
<Route path="projects/export" component={TaggerExport}/>
<Route path="projects" component={TaggerProjects}/>
<Route path="projects/add" component={TaggerAdd}/>
<Route path="projects/overview" component={TaggerOveriew}/>
<Route path="projects/visualize" component={TaggerVisualize}/>
<Route path="projects/errors" component={TaggerError}/>
<Route path="projects/:orgName" component={TaggerOrg} />
<Route path="projects/:orgName/:projectName" component={TaggerOrgProject} />
<Route path="projects/:orgName/:projectName/space" component={TaggerSpace}/>
<Route path="projects/:orgName/:projectName/export" component={TaggerExport}/>
<Route path="projects/:orgName/:projectName/overview" component={TaggerOveriew}/>
<Route path="projects/:orgName/:projectName/visualize" component={TaggerVisualize}/>
<Route path="projects/:orgName/:projectName/contributors" component={TaggerContributors}/>
{ /* Catch all route */ }
<Route path="*" component={NotFound} status={404} />
</Switch>
);
Hello in my directions file I set my struct
header
navbar
and my switch
foote
const AppRouter = () => (
<BrowserRouter>
<Route path="/login" component={AuthPage} exact={true} />
<Route path="/dashboard/addProduct" component={AddProduct} exact={true} />
<div>
<Header/>
<Navigation/>
<Container maxWidth="lg" >
<Switch>
<Route path="/" component={LandingPage} exact={true} />
<Route path="/xd" component={AuthPage} exact={true} />
<Route component={NotFoundPage} />
</Switch>
</Container>
</div>
</BrowserRouter>
);
But I have two routes where I didn't want to show my header
footer
and nav bar
which are the login and addproduct routes
how could i do that?
This is a little bit hacky (just to match your current approach) since I'm assuming you just want to hide those components in only those 2 routes, You can use withRouter in your Header and Navigation components, withRouter will provide to you the location prop and you can have a condition like this:
import React from "react";
import { withRouter } from "react-router-dom";
const Navigation = props => {
const { location } = props;
const { pathname } = location;
if (pathname !== "/login" || pathname !== "/dashboard/addProduct" ) {
return (
// Component logic goes here
);
}
// if we're on those routes we should return null to not render anything
return null;
};
export default withRouter(Navigation);
If you want a more robust long term approach this answer could help:
How to hide navbar in login page in react router
When a user completes a booking process and navigate to the confirmed details view. I need the URL to change. This is proving to be difficult to work around as the routing in done through MemoryRouter which can neither read, nor write to the URL. I need to break one of the views out and have the browser navigate to this new view.
I have tried breaking out from one router and creating a second that would return based on the original URL, then tried the very hacky window.location and direct the url to the new router.
import React from 'react';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import {
Page,
StartScreen,
StoreSearch,
ServiceSelector,
StoreSelector,
OptionSelector,
AppointmentForm,
AppointmentDetails,
ConfirmationScreen,
ErrorScreen,
} from 'components';
import { WithPageTitle, ScrollToTop } from 'containers';
import { services } from 'utilities';
const NewAppRouter = () => {
return (
<MemoryRouter>
<ScrollToTop>
<Switch>
<Route exact path="/" component={StartScreen} />
<WithPageTitle>
{pageTitle => (
<Page pageTitle={pageTitle}>
<Route path="/zip" component={StoreSearch} />
<Route path="/services" component={() => ServiceSelector({
services: services.services,
withBackButton: true,
backTo: "/zip"
})} />
<Route path="/stores" component={StoreSelector} />
<Route path="/options" component={OptionSelector} />
<Route path="/form" component={AppointmentForm} />
<Route path="/details" component={AppointmentDetails} />
{/* <Route path="/confirmation" component={ConfirmationScreen} /> */}
<Route path="/error" component={ErrorScreen} />
</Page>
)}
</WithPageTitle>
</Switch>
</ScrollToTop>
</MemoryRouter>
)
}
const AppRouter = () => {
if(window.location.href="http://localhost:9998"){
return (
<NewAppRouter />
)
} else if (window.location.href="http://localhost:9998/confirmation") {
return (
<ConfirmRouter />
)
} else {
return console.error('Route Not Found')
}
}
export default AppRouter;
I need a dynamic routing in my Reac app that use reac-router v4.
I want the following adress to build
http://localhost:7777/oceanside-yaht-club/booking/search
where oceanside-yaht-club can dynamicly change, depending on our customer.
But this is what I get now whit my try:
http://localhost:7777/oceanside-yaht-club/:pathParam/booking/search
Here is how I tried:
export enum Routes
{
Home = '',
BookingSearch = 'booking/search',
BookingOffers = 'booking/offers',
BookingExtras = 'booking/extras',
BookingContactDetails = 'booking/contact',
BookingBoatDetails = 'booking/boat',
BookingPayment = 'booking/payment',
BookingComplete = 'booking/complete',
BookingClose = 'booking/close'
}
export module Urls
{
export const home = `/${Routes.Home + ':pathParam?'}`;
export const bookingSearch = ':pathParam' + `/${Routes.BookingSearch}`;
export const bookingOffers = `/${Routes.BookingOffers}`;
export const bookingExtras = `/${Routes.BookingExtras}`;
export const bookingContactDetails = `/${Routes.BookingContactDetails}`;
export const bookingBoatDetaisl = `/${Routes.BookingBoatDetails}`;
export const bookingPayment = `/${Routes.BookingPayment}`;
export const bookingComplete = `/${Routes.BookingComplete}`;
export const bookingClose = `/${Routes.BookingClose}`;
}
export const IndexRoutes = () =>
<React.Fragment>
<Route exact path={ Urls.home } component={ App } />
<Route exact path={ Urls.bookingSearch } component={ Search } />
<Route exact path={ Urls.bookingOffers } component={ Offers } />
<Route exact path={ Urls.bookingExtras } component={ Extras } />
<Route exact path={ Urls.bookingContactDetails } component={ ContactDetails } />
<Route exact path={ Urls.bookingBoatDetaisl } component={ BoatDetails } />
<Route exact path={ Urls.bookingPayment } component={ Payment } />
<Route exact path={ Urls.bookingComplete } component={ Complete } />
<Route exact path={ Urls.bookingClose } component={ WindowsCloseDialog } />
</React.Fragment>
and this is how I push in the router history
const url: string = `${this.store.state.marinaData.MarinaUrlName}/${Urls.bookingSearch}`;
// const url: string = `oceanside-yaht-club/${Urls.bookingSearch}`; //for test
this.props.history.push(url);
I even tried this regex, but not figured out how to implement in my code
thnx
You cannot use the Route path param as it is directly while passing url to history.push, instead it needs a complete link.
You can change the code to
const url: string = `${this.store.state.marinaData.MarinaUrlName}/${Routes.BookingSearch}`;
this.props.history.push(url);