Why would I want to nest my React Routes? - javascript

I am having a hard time understanding why I would want to nest my react routes through components. In the following example you have two components spread across multiple files.
File 1:
<Route path='/topics' component={Topics} />
File 2:
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<Link to={`${match.url}/exampleTopicId`}>
Example topic
</Link>
<Route path={`${match.path}/:topicId`} component={Topic}/>
</div>
)
I am currently building a CRM for my company, where instead I am doing this:
routes/index.js
const routes = [
...
{ path: '/app/analytics/competitors', component: Competitors, meta: { title: 'Competitor Data', description: '' } },
{ path: '/app/analytics/competitors/:id', component: CompetitorById, meta: { title: 'Competitor Report', description: '' } }]
export default routes
App.js:
<Provider {...RootStore}>
<Router history={RootStore.routerStore.history}>
<Switch>
{routes.map((route) => {
return (
<Route
key={route.path}
path={route.path}
component={route.component}
exact={true}
meta={route.meta}
/>
);
}
})}
</Switch>
</Router>
</Provider>
When you have dozens of routes, I find this to be much clearer to read, implement and understand what is going on with my router.
It is more similar to the way I have seen routes implemented in other frameworks, such as VueJS's Vue StoreFront.
Is there anything wrong with what I am doing here, and if not, why do people nest their routes?
Thanks :)
Edit:
To further extend, I am using the routes as following for anything that required authorisation:
return (
<AppRoute
key={route.path}
path={route.path}
component={route.component}
exact={true}
meta={route.meta}
/>
);
This named route are being declared like so:
const AppRoute = ({ ...props }) =>
RootStore.userStore.hasSession ? (
<Theme>
<Route {...props} />
</Theme>
) : (
<Redirect to={"/login"} />
);
Within theme we have shared components that are used between every page, such as the navbar and header. This prevents re-rendering, so in essence each route is a container rather than a whole page.

One short but important reason would be re-rendering.
If you've got one route rendering under /foo and one under /foo/:id, then React would not need to re-render the foo route compinent, even when you switch from one id to another. If you don't nest them, everything has to be re-rendered again.
This has even more benefits when you want to add some simple rules, e.g. only allow users to enter /foo when they're authenticated. You can add this rule to your /foo route component. If a user is authenticated, the content is rendered, as well as all sub-routes become available. Without nesting, you would need to implement it in every route again.

Related

React 18 [MainLayoutRoute] is not a <Route> component

I use the MainLayout component to add the Navbar to the required pages and redirect the user to the login page if he's not logged in.
However now since I updated to react 18 and Component in Route was replaced by element. this part is no longer working and threw an error
Uncaught Error: [MainLayoutRoute] is not a <Route> component.
All component children of <Routes> must be a <Route> or <React.Fragment
const MainLayoutRoute = ({ path, component: Component, render, ...rest }) => {
return (
<Route
path={path}
{...rest}
render={(props) => {
if (!auth.BasicAuth())
return (
<Redirect
to={{
pathname: "/login",
state: { from: props.location },
}}
/>
);
return Component ? (
<>
<Navbar />
<Component {...props} />
</>
) : (
render(props)
);
}}
/>
);
};
A Route component is used in React applications to define a path that the app should respond to. When the app’s URL matches the path specified by the Route, the component that is associated with that route will be rendered. This allows developers to create dynamic, single-page applications that can render different content based on the current URL.
On the other hand, MainLayoutRoute is not a Route component. It is a component that is used to wrap other components and provide them with a common layout. This is useful when building applications with multiple pages, as it allows the developer to define a consistent layout that can be applied to all pages.
Find more info here - https://moderncodr.com/react-18-mainlayoutroute-is-not-a-route-component/

Programmatically creating routes from object does not work when doing it within a component

I am attempting to create a standard component for my application that returns a list of compatible routes with paths and children components systematically derived from an object featuring all of my application pages (eg. /log-in).
The issue is, I can place the route-returning script directly into the root react component, but when breaking that same code out into its own component and rendering in the root, it does not return any children, even though it returns the same thing.
This may have something to do with React route match, but I am not sure.
Example:
The object with all my pages:
...
const views = [
{
path: "/auth/sign-up",
exact: true,
component: SignUp,
secure: true
},
{
path: "/auth/sign-in",
exact: true,
component: SignIn,
secure: true
},
]
The Viewer component meant to catch the paths and return the compatible routes:
As an example, I am filtering based on whether the value of secure is true or not from the object above.
function Viewer(props) {
return (
views.filter(view => view.secure === props.secure).map((view, index) => (
<Route
key={index}
path={view.path}
exact={view.exact}
children={<view.component />}
/>
))
)
}
My root react component, featuring the Viewer component (From above), which does not work:
export default function Root() {
return (
<switch>
<Viewer secure={true} />
</switch>
);
}
...
My root react component where the routing code is placed directly inside, which does work:
export default function Root() {
return (
<Switch>
{views.filter(view => view.secure === true).map((view, index) => (
<Route
key={index}
path={view.path}
exact={view.exact}
children={<view.component />}
/>
))}
</switch>
);
}
...
Can you please assist me in understanding the difference between these two examples, the one using the routing code placed directly in the root element versus the root where it features the Viewer component which should do the same thing. Any info you can spare would be appreciated.
The cause of your issue is related how react-router-dom works, I mean Switch component is intended to render just one Route component at time, so the children for Switch component must be directly a Route or Redirect component instead of a wrapper for that Routes, if you want to do something like that with a Viewer component, you should move your Switch component inside your Viewer component, something like this
function Viewer(props) {
const routes = views.filter(view => view.secure === props.secure).map((view, index) => (
<Route
key={index}
path={view.path}
exact={view.exact}
children={<view.component />}
/>
));
return <Switch>{routes}</Switch>;
}
Here you have the official documentation that tells you about that and explain how it works.
Switch component will iterate over all its children to figure out if the Route should be render or not related to the url path, so for that reason is mandatory that the direct children of a Switch component must be Route or Redirect components cause these contains all the props , path is one of these props, so it is used to match the current url path to be compared with the path prop of the Route component.

react-router-redux redirect to absolute url

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" />}
/>

React Router V4 protected private route with Redux-persist and React-snapshot

I'm implementing private route like so using React Router Route Component:
function PrivateRoute({component: Component, authed, emailVerified, ...rest}) {
return (
<Route
{...rest}
render={props =>
authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/', state: {from: props.location}}} />}/>
)
}
Expected Behavior:
authed is persisted through page refresh through using redux-persist
So on page refresh or reload, if authed prop is true then router should render <Component /> without ever going to path "/"
Actual Behavior which is the Problem:
With authed === true (persisted)
reloading the page or refreshing it leads to the following actions taking place(checked redux devtools)
the action:
"##router/LOCATION_CHANGE" runs and takes it to the correct secure route but then "##router/LOCATION_CHANGE" runs again and it redirects to "/" for a moment and finally
"##router/LOCATION_CHANGE" runs again and directs route back to the secure path, even though authed === true through all this in the redux devtools
Then: My guess was that this error has something to with my main App Component rendering before redux-persist has time to re-hydrate the Redux store.
So I tried doing the following:
I tried delaying my main App component render until my store is re-hydrated using redux-persist like so:
render() {
const {authed, authedId, location, rehydrationComplete} = this.props
return (
<div>
{ rehydrationComplete
? <MainContainer>
<Switch key={location.key} location={location}>
<Route exact={true} path='/' component={HomeContainer} />
<Route render={() => <h2> Oops. Page not found. </h2>} />
</Switch>
</MainContainer>
: <div>...Loading </div> }
</div>
)
}
This effectively fixes the issue above of the path changing when "##router/LOCATION_CHANGE" action runs(only Changes the path keys), However this leads to another Issue with React-snapshot Now: all the static generated html files from react-snapshot Now contain only ...Loading. I tried to set snapshotDelay of 8200 in the react-snapshot options but that didnt solve the issue.
Then:
I tried the following to delay React-snapshot call so that it renders html after the store has been rehydrated:
import {render as snapshotRender} from 'react-snapshot'
import {ConnectedRouter} from 'react-router-redux'
async function init() {
const store = await configureStore()
snapshotRender(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('root')
)
registerServiceWorker()
}
init()
But now i get the error: that 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?
I know this is a loaded question, but I want to effectively use these 3 libs(React-Router V4, Redux-persist, React-snapshot) together to serve protected routes without the mentioned errors.
I have something similar to you. Here I use React-Router V4 and a persist-like library.
Your router/routes doesn't need to be aware of the persist. They should rely on your redux's store. The persist should rehydrate your store with all the data.
I didn't see where you are using the PrivateRoute component in your example. Where is it?

Multiple child routes in React-router

I am trying to implement a router where a Route has two parallel/independent sub-routes and I wonder if this is possible with react-router. The image below illustrates what I want to achieve. It represents an event-editor with two components: (1) a list of events (2) an editor for one event. I think their states should both be part of the URL somehow.
One extra constraint: [child1] should be aware of the state of [child2], since it needs to show the item that is currently edited in a different color or so.
If this is not possible with react-router, how would you implement this? Thanks!
Look this. By doing this you can create a parallel control of routes, without harming root.
/**at index.html*/
< div id="root"></div>
< div id="helper"></div>
/**at index.js*/
ReactDOM.render(
(
<BrowserRouter>
<App/>
</BrowserRouter>
),
document.getElementById('root')
);
ReactDOM.render(
(
<BrowserRouter>
<Helper />
</BrowserRouter>
),
document.getElementById('helper')
);
/**at Component Helper*/
render() {
const location = { pathname: '/2' }
return (<Switch location={location}>
<Route exact path='/2' component={Welcome} />
</Switch>);
}
const Welcome = () => <h3>welcome helper</h3>
//I'm using router V4 to switch of the routers.

Categories

Resources