Say we have Home, Page A, Page B and Page C. If I do this:
Open Page B
Go Home
Go to Page B
This pushes at least 3 routes in the history object. If I repeat the steps, there will be 6 items. This is true when pushing directly via history.push and also when using the Link component's with the to prop.
The only way to keep complexity under control is by checking the previous location and then doing either history.goBack or history.push: Check history previous location before goBack().
The catch is that managing the history object can become a very complicated task very quickly. Just by adding a navigation bar that is rendered on every page in the app, you add at least "n-1" places from where you can go back home (assuming home is one of the navigation tabs).
Should we be concerned about this and handle the previous location?
Possibly related question: Why does the React Router history length increase on refresh?
Environment:
history is BrowserHistory
react-router#5.1.2
react-router-dom#5.1.2
Thanks to Elan Hamburger's comments, I now understand that the BrowserHistory is meant to behave like this. Normally, in the browser, if you go to page A, then to page B, then back to page A, clicking back 3 times will return you to the home page.
So you shouldn't really mind the size of the history object, at least certainly not if you don't have billions of users.
Related
Is it possible to view the list of paths in the history object? I can see that there is the length but I need to see the actual paths.
The reason for this is for debugging purpose. I have 2 views (SPA) that gets displayed via their corresponding URL. But my problem is when I go from the first page to the next page and then I wanted to go back to the first page, I need to click the browser back button 2 times before it goes back to the first page. That's the reason I wanted to see what are the paths in the history object so I can check what entries are being added because as far as I can see, the code I have to add a path to the history object is straightforward:
props.history.push("/classes/" + courseId);
That's just the code so I don't know why I need 2 clicks to go back to the previous page.
Not exactly sure how to see the history stack, but you might be unintentionally rendering the page twice, so the history.push is working as intended. You can log in componentDidMount to see if it does, and be careful when using useEffect because that can cause that as well. If you're using react router check how the route is added in your app.js (for ex do <Route path="/classes/:courseId" component={courseId}> instead of <Route path="/classes/:courseId" render={()=>courseId()}>.
I am trying to implement the React Router in such a way that it supports a route like this:
/myPages/:pageName1/:pageName2/:pageName3/...
The idea is that, even though the page being rendered is only the last page, the pages are nested, and the depth is not something that is pre-determined. The component that renders the actual page needs to know the names of parent pages.
To clarify, the idea is that the page data in the backend are in a tree structure in such a way that, in the above example, page1 is the root page, page 2 is a child of page1, page 3 is a child of page2, etc. In addition, one page can have multiple children. The last child name (so pageName3 in the example) is what is being used to query the database and get all the content required to render the full page. The parent names are required to render navigation-related subcomponent. I should also mention that just having /myPages/:pageName3 and getting all parent names from the backend is not really an option. I could fetch that information from the backend, but the URL presented to the user still needs to have that structure.
I am hoping that there's a way to get this type of information as an array, but I am having a hard time finding an example like this on the web.
maybe this can help.
https://github.com/ReactTraining/react-router/blob/d28d46dce08a5756a085f7e5eebb5169ea59e40b/packages/react-router/docs/api/Redirect.md#from-string
states:
A pathname to redirect from. Any valid URL path that path-to-regexp#^1.7.0 understands.
maybe you can combine
<Redirect from='/users/:id' to='/users/profile/:id'/>
with
var re = pathToRegexp('/:foo+', keys)
(taken from https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#one-or-more)
then you'll end up with
<Redirect from='/:pageName+/:id' to='/:id'/>
I am using react-router in a SPA.
In my case,
the browser history is /home => /somepage1 => /another => /changepassword
when I do some thing in the route /changepassword, I wanna go to the /login route, and clear all the previous browser history and make this route /login the 1st in the history.
How can I achieve this?
TLDR… it can be done in some conditions.
A Solution when creating browser history
My App is loaded in a new browser window, thus I'm certain the app will have a clean browser history, so I can use this to get back to the first page in the browser history:
props.history.go(-(props.history.length - 1))
If page you want to return to in the browser history was not at position 1, you can record the value of history.length at this Start page and then pass that number into this function when you're ready to go back to the Start page.
// loading start page…
const startPageIndex = props.history.length - 1
// later when ready to go back to start page…
props.history.go(-startPageIndex)
Note: When going back by using either of the above solutions, the "forward" history will still exist until the user navigates forward to create new history.
MemoryRouter - doesn’t store browser history
Use the <Memory Router> which "keeps the history of your “URL” in memory (does not read or write to the address bar)" for the flow of pages where you wish to replace the history… because it will not create a browser history.
Further Reading
Michael Jackson, the author of react-router, posted this answer regarding Reset/clear history:
There's no way for us to programmatically reset the history stack (at least in a browser, in memory history this would be trivial). If you need to clear the history stack for some reason, I'd suggest you use window.location.reload().
Further from MDN:
There is no way to clear the session history or to disable the back/forward navigation from unprivileged code. The closest available solution is the location.replace() method, which replaces the current item of the session history with the provided URL.
remove all history entry
this.props.history.index=0
use in componentWillMount function where you want this will be First entry
I don't believe there is elegant support for this. History is mutable however, so you can always edit history manually. Here's an example of how you might do that:
const history = this.props.history.entries
// set first entry in history to mirror the last entry
history[0] = history[history.length - 1]
// remove all but first history entry
history.length = 1
I currently have a button that onClicks to history.goBack but I want it to direct the user to another path if there isn't a previous page (in the case the user visits the page directly by typing in the URL in the URL bar instead of clicking within the site to get there). When I checked out this.props.history there isn't an object containing the history of pages visited or a boolean that tells me if there is a page for history.goBack to work on. How do I check that? Looking at the docs for history library, there is suppose to be an entries property but this somehow didn't make it into react-router.
You can use go() property to move further back.
Eg. go(-1) will be equivalent of goBack() and go(1) will be equivalent of goForward().
I don't think there is a way to list all the paths on the stack, but you could allways implement history that you can push to and control.
In my application I'd like to have certain portions of the app not be able to deep linked to. For example our users have a list of surveys and I'd like if someone tried to go directly to a particular survey directly such as /survey/1 that react router would pick up on this and immediately redirect them back to /survey and they would have to select the one they want. I've tried to write onEnter hooks but they seem to be very cumbersome since the only way I've been able to get them to behave correctly is to store some global state that says they have been to the main page and inspect that every time the route is navigated to.
Im using pushstate in my application if that makes any difference and react-router 2.0
I'd like to try to avoid having to write server rewrite rules for this since there are a lot of areas in my application where this rule is applicable.
I have a suggestion which is similar to the onEnter hook:
Wrap the component of the survey/:id route with a function which verifies if deep linking is allowed or not, let's call this function preventDeepLinking
The preventDeepLinking function checks if the location state contains a certain flag, let's say allowDeep. This flag would be set in the location state when navigating from another page of your app. Obviously, this flag will not be set when the user tries to navigate directly to the page of a survey.
The preventDeepLinking function will render the wrapped component only if deep linking is allowed, otherwise will redirect to a higher route.
I created a sample on codepen.io. You can play with it in the debug view of the Pen: http://s.codepen.io/alexchiri/debug/GZoRze.
In the debug view, click the Users link and then on a specific user from the list. Its name will be displayed below. Notice that its id is part of the url. Remove the hash including the ?_ and hit Enter. You will be redirected to /users.
The code of the Pen is here: http://codepen.io/alexchiri/pen/GZoRze
The preventDeepLinking function can be improved, but this is just to prove a point. Also, I would use the browserHistory in react-router but for some reason I couldn't get it running in codepen.
Hope this helps.