react-router: how to get the previous route in onEnter handler? - reactjs

I'm trying to find a way to read the previous route/path when a user hits a new one, within the onEnter handler.
I have a React Router structured like so:
<Router history={history}>
<div className="index">
<Route
path="/"
component={ComposedAppComponent}
onEnter={this.onEnterHandler.bind(this)}
>
<Route name="streamKey" path=":streamKey">
<Route name="articleUri" path="(**)" />
</Route>
</Route>
</div>
</Router>
the function, onEnterHandler, looks like so:
onEnterHandler(nextRouteState) {
const { streamKey, splat } = nextRouteState.params;
const nextPath = `/${streamKey}/${splat}`;
const prevPath = // HOW DO I GET THE PREVIOUS PATH?
}
I can't seem to find a way to read the previous route path the user was on... I need to make a comparison between the new route and previous one. Any input on how to approach this is much appreciated. :)
Cheers!

Are you trying to get the previous path when the user navigate to new path through address bar or using react-router like this.context.router.push()?
If the navigation is using react-router, maybe these simple way would get what you're looking for:
1. Pass prevPath using query-params
Navigation might look like this:
`<Link to="/next" query={{ prevPath: this.props.location.pathname }}> Your Next path</Link>`
Now, you can get your prevPath value on onEnterHandler method by using this:
nextState.location.query.prevPath
Cons: the query will appear on address bar.
Pros: you are still able to get your prevPath when user is navigating through address bar (direct path access).
2. Pass prevPath using state
Navigation might look like this:
`<Link to="/next" state={{ prevPath: this.props.location.pathname }}> Your Next path</Link>`
Now, you can get your prevPath value on onEnterHandler method by using this:
nextState.location.state.prevPath
Cons: the query won't appear on address bar.
Pros: you are not able to get your prevPath when user is navigating through address bar (direct path access).
In addition, the cons of those method is you should assign a prevPath value into each of Link components. Create a wrapper component for the Link component would solve this issue.

Related

Show/Hide route components in react

I am working on a React application which has routes like so:
<Switch>
<Route path="/edituser/:username" component={EditUser}/>
<Route path="/createuser/:type" component={EditUser}/>
<Route path="/listusers" component={ListUsers}/>
</Switch>
ListUsers component shows a table with pagination where each component in the table has a link which points to /edituser/:username.
I can edit users by clicking on the item in the table but as expected with react, once I go back to listusers/ the component is loaded again and I will be on the first page of users. I want to be on the page from where I accessed the user in the first place.
What is the best pattern to achieve this? I thought about passing in the page number to /edituser and then back to /listuser but then again I have to load all the paginated results again. Is local storage the only option? Any pointers are much appreciated.
There are two solutions for your problem:
1) Pass last active page ad route parameter and set your pagination accordingly.
<Switch>
<Route path="/edituser/:username" component={EditUser}/>
<Route path="/createuser/:type" component={EditUser}/>
<Route path="/listusers/:pageNumber" component={ListUsers}/>
</Switch>
on your componentDidMount you can use it to set state. For example :
const pageNumber = this.props.match.params.pageNumber;
2) Pass state prop in your routing. For example :
<Link
to={{
pathname: '/listusers',
state: { pageNumber: 1 }
}}/>
on your componentDidMount you can use it to set state. For example :
const pageNumber = this.props.location.state.pageNumber;
You could add the page number to the URL in the /listusers endpoint. Maybe something like /listusers/2 or /listusers?page=2 this way, when you hit the browser's back button, you're directly there. One last thing you could do but I wouldn't advise in this case is to store the page number in the history state.
As a rule of thumb, in order to get back, prefer using the history than using local storage.
If you really want to keep the data in memory, you can always use a store that is in a higher component (the root component for example) and keep the previous query over there. However you'll need to be careful about a lot of routing issues in such cases:
cache invalidation: the data changed on the server side in the mean time
user somehow gets back to a page with another table page number
loaded user presses the browser back button

How to access value of a routing parameter in react?

I want to have a 'dynamic route' for a section of my app, which was done like this:
<Route path="path(/:id)" component={Component} />
So far this works, but in Component I would like to access the value of id because it changes a bunch of things depending on which it is. How can I do that?
In your component, you would access this via props.
this.props.params.id
Here is the guide from react-router that goes into more detail as well.
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/06-params

Using multiple params with React Router?

<Route path="/:user" component={Home}>
<Route path="/:thing(/:version)" component={Thing}/>
</Route>
So, I've got two dynamic objects in my application that I'd like to be controlled by route params in react-router. Using the code above, both /0 and /0/3 take me to Home. I need /0/3 to take me to Thing. I'm not sure what I'm doing wrong here... Does react-router even support multiple dynamic params next to each other like this? I couldn't find anything in the docs.
What happens here is that you've given React Router two paths that can both match on /anything. By default then React Router matches the first one it can find.
To dig deeper, if I go to /pudding, React Router can't know if you meant /:user or /:thing. Since /:user occurs first, that option will be chosen.
You also need to make sure if nesting routes is what you want. Currently, your Thing route is nested below Home, which means that it is rendered via this.props.children in your Home component. So, for your current Thing route, Home will always be rendered too, with Thing as a child. If your Home component doesn't render this.props.children, Thing will not be shown.
I suspect you just want two different pages. What you could do to achieve that is the following:
<Router history={history}>
<Route path="/user/:user" component={Home} />
<Route path="/:thing(/:version)" component={Thing}/>
</Router>
This will make every /user/name go to the Home component, and every other /random (with an optional extra level) will go to Thing. If you wonder why in this case React Router doesn't take /user/name to the Thing route, it's because it still matches in the order your routes are specified. Because your Home route matches the requested URL, no siblings of this route are tested anymore.

React/Redux Where to save State

I am having trouble understanding some ReactJS and Redux principles,
In Redux we have a store and a set of reduceres that give your final state ( this part I get).
A Component receives props from their parent component, in case of Redux it passes the full state down with the Provider Component.
Now, lets assume this use case, I have my app state something like this :
auth : { name,id, ..etc} -> this is actually a JWT Token with a set of claims...
I am building a Container (Page) to be able to edit user profiles, the catch here , and where I am having trouble, I also want this page to handle a arbitrary user edit (in case admin is editing a account).
I am rendering my Page by using redux-router like this :
<Route path="/user" component={RequiresAuth(UsersPage) } >
<IndexRoute component={RequiresAuth(UsersOverview) }/>
<Route path="/user/overview" component={UsersOverview} />
<Route path="/user/account" component={AccountManagement} >
<IndexRoute component={RequiresAuth(AccountManagement) }/>
<Route path="/user/account/:userid" component={AccountManagement} />
</Route>
So reading the docs, I assume my props will have a userid which will override my default user if present.
Questions:
Is this approach correct? The Route Handling?
If I pass a userid param on the route, how do I proceed to load the user info and where? The way I see it, I need to dispatch an action, but I am not sure where to set the state , if on the store, or the component.
Also, Where would I load my user information? (constructor comes to mind)... or should it be in WillMount/WillReceiveProps?
Thank you.
If you really have a hard time to understand React/Redux principles i suggest you to try this tutorial :
https://github.com/happypoulp/redux-tutorial
It helped me a lot when i first started with React/Redux. By the way it's a bit hard to really answer your question because you re asking specific questions on a really specific case :)

ReactJS - two way router

I wanted to ask if something like two way router. I found the router-react, but I do not like setting of Link directly to path in string format. Like
<Link to="/about">About</Link>
Exist router to refer to a component instead of the path? Like
<Link to={AboutComponent}>About</Link>
If you would like to change the URL, so I had to change paths everywhere. Thus, I will change it in one place.
Exist router to refer to a component instead of the path?
No.
If you would like to change the URL, so I had to change paths
everywhere. Thus, I will change it in one place.
Then store all your paths as a constant in one place, and import them where you need them:
// paths.js
export const ABOUT = '/about';
export const SOMETHING_ELSE = '/somethingelse';
So in your app:
import { ABOUT, SOMETHING_ELSE } from './paths'
...
<Route path="{ABOUT}" component={AboutComponent}/>
And just do the same wherever you use <Link to={ABOUT}>About</Link>
If you at some later point decide to change the path, just change it in paths.js.
IMO, we cannot use component itself directly in Link component, simply because in some case one component may be shared in different routes. Like the following:
<Route path='/recent-movies' component= {MovieList} />
<Route path='/hot-movies' component= {MovieList} />
In the above example, the MoveList component will display the movies based on the path passed in. (I knew it is not a good example, just want to show it is a possible case)
In angular ui-router, it introduced route name, so every time it will not be affected when path is changed. But in react-router, it is simpler, I personally don't mind to much.

Resources