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
Related
I currently have a "go back" link on a page that currently uses router.back() in the onClick handler to return to the previous page, which normally works if the current page was navigated to within my site, but if the page was navigated to directly (say, via a bookmark or a pasted URL) then I would like the "go back" link to do a router.push('/') to go back to my home page instead. I don't know how to determine if the previous browser history URL is outside my site to do the router.push() instead of the router.back(). Anyone have any suggestions?
You can use window?.history?.state?.idx to check the index of the state representing the latest visited page.
If window?.history?.state?.idx > 0, that means that the user's been navigating on the website and you can "safely" use router.back(...).
If the url is directly pasted in the browser, this value will be equal to 0.
You can check out document.referrer and check that the URL's hostname matches yours. If it does, you can safely use router.back(). If it doesn't, you can use router.push('/').
Unfortunately, document.referrer doesn't work with Next correctly.
The preferred method would be utilising a session storage to save previous and current paths using useEffect hook in _app.js
...
const storePathValues = () => {
const storage = globalThis?.sessionStorage;
if (!storage) return;
// Set the previous path as the value of the current path.
const prevPath = storage.getItem("currentPath");
storage.setItem("prevPath", prevPath);
// Set the current path value by looking at the browser's location object.
storage.setItem("currentPath", globalThis.location.pathname);
}
useEffect(() => storePathValues, [router.asPath]);
...
With this, you can evaluate from which page the user came and then utilize either router.back() or router.push().
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()}>.
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.
The react app has search page. There are input.
The path is 'search/:query', and by default you see zero results.
If you go to 'search/star%20wars' you will see some results. In componentDidMount() I added if statement to load result if match.params.query is not null.
If I type into search input Spider Man and click submit - I trigger a search and show results. But if you reload page - you will see the result about Star Wars. So how update match.params.query? Or may be there other solution of fix this.
You need to update the history object as well.
What you are doing is altering the history object available to you and calculating the results based on that object. But when you will refresh the page it still holds the original history object.
One way of doing it, you need to push or replace a new route in the history.
Because evert search page is a new page, so if you want the previous pages to stay preserved you should use history.push otherwise history.replace
Implement it like this:
var routeObj = {
pathname: samePath,
state: sameState,
query: newQuery
}
//push it in your history using which ever routing library you are using.
//For Example:
router.history.replace(routeObj);
Note: Do not worry about rendering speed on changing the history. React is smart enough to handle that. Basically whenever you will push a route whose component is already mounted it will not unmount and remount the same component again, rather it will just change the props and will re render it.
The callback for this case will be => componentWillReceiveProps
#misha-from-lviv The way I see your problem statement is that you have two source of truth on is the query params, using which you should update your state, and the other is the default state which is populated from the default value of your filters.
As #Akash Bhandwalkar suggested, you do need to update the route in using the History API. But also you also a need a top-level orchestrator for your application state, which will allow you to read and write to the history api ( change your route ) and also do an XHR / fetch for you to get the results.
How I'd approach this is that I'd start with a Parent component, namely FiltersContainer , which actually does this orchestration to read and write to the url. This Container would have all the side-effect knowledge for fetching and updating the routes ( error handling included ). Now the all the child components ( filters and search results maybe ) will just read the state thus orchestrated and re-render.
Hope this guides your thinking. Do revert here if you need further guidance. 😇
Cheers! 🍻
I have the following scenario:
A user opens an activation link; after the user has completed the activation process, the system will move them to another page.
I don't want to keep the activation link in the browser's history because when the the user goes back they will get to the activation step again.
How do I replace the history of a browser to remove certain requests from my application?
In ReactJs you should use browserHistory for this purpose. This takes care of your histories and you don't need to implement those functions on your own.
browserHistory has 2 methods push() and replace() which do the same functions as #fazal mentioned in his answer but in a better way.
So if you want to avoid user going back to previous state you would need to use browserHistory().replace
Start with importing it into your code:
import {browserHistory} from 'react-router'
After user has activated you do following:-
browserHistory.replace(//your new link)
HTML5 history API provide two methods to Adding and modifying history entries.
pushState() : back state preserved
replaceState() : no back state
Assuming you are using react-router.
So, on your Component use
this.props.history.replaceState('your new state')
read more: Manipulating the browser history
video: REACT JS TUTORIAL #6 - React Router & Intro to Single Page Apps with React JS
I know this is old but I had a similar issue and none of the other answers were 100% applicable with my version or React but this should work in more recent versions by clearing appended paths.
//replace(path, state)
this.props.history.replace("/home", "urlhistory");
Run this block where you want change route
history.entries = [];
history.index = -1;
history.push(`/route`);
This will clear the history and change for a new one.
window.location.href = 'Your path';
or
document.location.replace().