What's the logic behind how React Router processes onEnter? - javascript

react-router has some docs on how onEnter/onLeave are handled, but I don't understand why traversing up the tree would not cause onEnter to be triggered?
For example, if you have the following routes:
/
/profile
/profile/:username
And navigated from / to /profile to /profile/boogers, onEnter would be triggered 3 times. But if you navigate to / from /profile or /profile/boogers, it's not triggered? I would expect that entering a new path with always trigger an onEnter event because you're going to a new url path?
Thanks for your time!
Related to this issue as well.

The routes are considered hierarchical. When you first navigate to /profile onEnter would be triggered on both /profile and /. When you then navigate from /profile to /, no onEnter is triggered because it is considered that you were already within that parent route.
So, I think that's technically the answer, but I also think it's dumb, IMHO. It does indeed leave you with some serious flaws, which the OP already pointed out in the linked github issue on react-router.
Related is the fact if you navigate from /users/a to /users/b, there is not a hook for that at all. Your only option to implement componentWillReceiveProps and check to see if userId has changed. Poor separation of concerns. More info: https://github.com/reactjs/react-router/issues/2547

Related

React Router - Unexpected Redirect Behaviour with multiple redirect components

I'm coming across the following scenario:
I have a react router wrapped react component rendered with 3 routes:
If the route matches "/about", redirect the user to "/about-new".
If a route is matched, display the associated component.
If the the route requested doesn't exist, redirect user to "/404".
see live example.
What I'm finding is that when I click on the link to navigate to "about" page, the route changes to /404 instead of "/about-new" and when I click on the link that goes to a non-existing route, it attempts to navigate to that route instead of "/404".
Is the expected behaviour for React Router? Is there something wrong with the way I defined my component? Or is this a bug with React Router?
You don't have a route for the About component.
https://codesandbox.io/s/react-router-v5-starter-forked-f715c?file=/src/index.js
In my search for the answer, I realize the behaviour of <Redirect> is lacking documentation. I've played around with it a bit to understand how it works.
Short Answer
Yes, this is the expected behaviour.
Long Answer
When <Redirect from='...' to='...'> is matched, React Router will attempt to find a matching <Route> within your application. This could be a match from your local switch or from a parent switch in the case of a micro frontend application.
From the code base, my understanding is that a Router is always trying to find a component to render; After all, the nature of React is to translate JSX elements into JavaScript and spitting out plain HTML.
If no route is matched, the redirect will be ignored and continue down the switch. This is why in my example <Redirect to='/404'> was matched.
As you can see from the code here, the <Redirect> component's job is to replace browser history and since it isn't really rendering anything, the router continues its search for some component to render.
In the scenario where there is no <Redirect> with only routes, and no route has been matched, it will fall back to changing the browser URL to what is stated in to={...} argument of the Redirect and display a blank page.
So the behaviour for React Router within a <Switch> is that routing only stops when a <Route> is matched or when a blank page is displayed when no <Route> is matched.

react-router-dom back with previous state

I have an app with react-router-dom and use the BrowserRouter component. In the app, I have route /query-builder that will essentially build a query string through picking values from tables etc. Once the user has the query string built, they can navigate to /search?q=<some querystring> via the useHistory() hook from react-router-dom and history.push('/search?q=${queryString}').
What I would like to know, is it possible to navigate back to the /query-builder route, via the browser back button, and see the page as it was just before I navigated to /search so the user could make amendments to the query. What I am seeing at the moment, is that the query-builder component will go back to its initial state, as it is mounting again.
I could use redux to manage the query builder state and rehydrate the components from that, but I am wondering am I missing something available in the react-router / react-router-dom packages?
Look like useContext is best variant to solve your problem. Save variables in query-builder, then you can easily go back without any scare) Also it'll work properly both in history.goBack() and browser back

Angular 2 dynamic routing doesn't work when try to access by URL

I am trying to archive dynamic routing in my Angular application. I have completed following functionalities so far.
Add routing to an existing angular component by user input.
Remove routing from an existing angular application.
I have used router.resetConfig to do this.
My problem is that whatever the new dynamic route I have added cannot be accessed by typing in the URL. it gives me following error.
ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'module3'
Error: Cannot match any routes. URL Segment: 'module3'
Here is my code
app.module.ts
const routes: Routes = [
{
path: '',
component: Module1Component
},
{
path: 'module2',
component: Module2Component
}
];
app.component.html
<div>
<h2><a routerLink="">Module1</a></h2>
<h2><a routerLink="module2">Module2</a></h2>
<h2><a routerLink="module3">Module3</a></h2>
<input type="button" value="add module 3 to route" (click)="addRoute()"/>
<input type="button" value="remove module 3 from route" (click)="removeRoute()"/>
</div>
<router-outlet></router-outlet>
app.component.ts
addRoute(){
let r: Route = {
path: 'module3',
component: Module3Component
};
this.router.resetConfig([r, ...this.router.config]);
console.log(this.router.config);
}
I have found that after trying to access the newly added route by typing it in the browser URL the newly added route object disappears from the route array. Hence I am getting that error.
Following image is before I add the dynamic route object
This is after I add the route object
But when I try to access the /module3 route by accessing it in the URL,
it just reinitializes the route object array and therefore it doesn't contain the newly added route.
My question is how I can persist this newly added route to the initial route object array. So that it will be there when I try to access it by URL.
I have added sample angular project for this scenario in the following GitHub repo.
https://github.com/mal90/ng-dynamic-route
If anyone can point me in the right direction is much appreciated!
Angular is a client side framework so when you add a new route dynamically it's only effective as long as you do not try to access a page from a server request, which is the case when you enter a URL in the browser
As David pointed out, your approach is precise headshot for Angular app. Looking at your profile, you seem to know about Node.js and you have rather good score, so I presume there must be good reason for you going with this highly specific road. If you really want to do such a crazy thing (I cannot see the reason why to do it ... I think you should rather use server-side solution) you might try storing the router definitions in some local storage or external file and force app to redirect any path to some specific route, then to force the app to redefine the router, initialise it again and only then navigate to the new component. As you can see, you will need a lot of force to do this. Some people had already similar ideas. Such as in this post about external definitions Do not take me wrong, I like crazy ideas. You could probably hack the environment somehow. But I think it is not worth it.

Navigate away from react-router?

My React app is currently nested inside of a Spring app, which means my routing is mixed. For example, /route/1 is handled by react-router, but /route/1/details is not.
I have a component which receives a destination URL from the server as props. Some destinations are inside the router and others are outside. In an attempt to safely use <Link /> in place of <a />, I have the following catch-all route handler:
{
path: '*',
onEnter({location}) {
window.location.href = location.pathname;
}
}
This works to get me out of react-router and back to Spring; however, it breaks the back button. Pressing back from here cycles through the URL history without ever reloading the page. (The history is all routes handled by react-router.)
Am I missing something obvious here? I think I want back to force a reload, but I'm not sure how. (I see that this is sort of an XY-problem; I'm open to suggestions for different ways of approaching the problem.)
I recognize this is not an ideal solution; it is one step in a migration plan.

.redirect() from mounted middleware with relative destination fails

I want to restrict a certain subtree only to authenticated users. The basic setup is as follows (fat removed):
app.use(express.bodyParser())
.use(express.cookieParser('MY SECRET'))
.use(express.cookieSession())
.use('/admin', isAuthenticatedHandler)
.use('/admin', adminPanelHandler);
Where the handler functions is:
isAuthenticatedHandler = function(req, res, next) {
if (!req.session.username) {
res.redirect('login');
} else {
next();
}
};
The problem is that even though I provide the redirect destination as a relative path 'login', it doesn't lead to <mount_point>/login i.e. /admin/login but to /login which of course throws a 404.
From the expressjs API reference:
This next redirect is relative to the mount point of the application.
For example if you have a blog application mounted at /blog, ideally
it has no knowledge of where it was mounted, so where a redirect of
/admin/post/new would simply give you `http://example.com/admin/post/new`,
the following mount-relative redirect would give you
`http://example.com/blog/admin/post/new`:
res.redirect('admin/post/new');
Am I misreading this?
The issue here is that while you are using your middleware off of /admin, your app itself is not mounted at /admin. Your app is still off of the root, and your configuration simply says to only use your isAuthenticatedHandler middleware if the request comes in off the /admin path.
I whipped together this gist. Notice how it uses 2 Express applications, one mounted inside the other (line 23 pulls this off). That is an example of mounting the application at a different point rather than just putting a given middleware at a given point. As presently written, that example will give you an endless redirect, since the isAuthenticatedHandler fires for everything off of / in the child application, which equates to /admin overall. Using 2 separate applications might introduce other issues you're not looking to deal with, and I only include the example to show what Express means when it talks about mounting entire applications.
For your present question, you'll either need to follow what Yashua is saying and redirect to /admin/login or mount your admin interface as a separate Express application.
What are you trying to achieve? Why not just redirect to '/admin/login' ? And the mount point they are talking about is the place where your Express app is located, not necessarily the current URL. So /blog might be setup on your server to be the root of your app while / might be a totally different app. At least that's the way I read this.

Categories

Resources