React-router: Change URL and clear history - reactjs

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().

Related

How to update match.params?

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! šŸ»

how to remove all the browser history by React-router-4

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

react router - keep the query string on route change

I would like to create routes that support query string.
When i say support i mean, passing it to the next route some how.
For example:
given this route: domain/home?lang=eng
and when moving to route domain/about i want it to keep the Query String and display domain/about?lang=eng.
I was sure there's a built in functionality for this but after reading the docs and a lot of search on the net, i couldn't find an elegant solution.
I'm using react-router#3.0.0 and react-router-redux#4.0.7
For react-router 4.x, try
const { history }
history.push('/about' + history.location.search)
To access this.props.history, make sure you have wrapped the component with withRouter HOC
import { withRouter } from 'react-router-dom'
...
export default withRouter(component)
refer https://github.com/ReactTraining/react-router/issues/2185
You will have to "forward" query param on each page transition - bothering and you can easily forgot to...
Instead, I would do this.
read stored/persisted lang preference. localStorage is good candidate here. Fallback to default language, when no preference is found
share lang via context, so that each and every component can read this value.
create some button (or whatever), which would modify this value
Since you are using redux, I would pull redux-persist to persist this preference across page reloads.

Using react-router-redux can Link components be used to transition after loading a routes state

I have been using react-router-redux which allows for my location to be stored in state nicely, and allows me to use react-router for defining routes, and linking between routes.
One problem seems to be that when I need to navigate using a <Link /> component like so:
<Link to={`/movie/${m.id}`}>
<p>{m.name}</p>
</Link>
this will trigger the change of route, and on load of the new route, in this case the <Movie /> route, the state for the movie page is loaded.
This doesn't really work nicely for transition between pages, as the new page will be blank on navigation, or you can put in a loader but really it should not navigate to the new page until the api call for the new page has loaded. Similar to sites like pitchfork.com or theguardian.com
This implementation has been discussed in this question and the approach of just using a loader between page transitions is used in the real-world-example and reddit api example
My question is, if I want to load state for the new container/route in my site before navigating, do I need to follow the approach suggested by dax chen in this question?
To sum up the answer was to fire an action and using redux-thunk we can preload the state by calling the action for the new container/route, then on completion we navigate to the page using react-router-redux's push() action
store.dispatch(push('/foo'))
If this is the recommended approach for preloading a page before transition, then I will be changing my react-router-redux-example and my current site to use this approach.
An action to load the new route's data then navigate will look something like this:
function fetchInit(url, id, to) {
return dispatch => {
return dispatch(fetchMovie(url, id))
.then(function(home) {
return dispatch(push(to))
})
}
}
It will need some logic to identify the new container's action to call and also the new path to navigate to. It may call the static fetchData() method on the new page's container component.
So this will somewhat complicate the <Link /> component, I am just looking for some advice on if this is the correct way, or I may have missed a simpler more recommended approach.
<Link onClick={ () => dispatch(navigate(`/movie/${m.id}`, 'Movie')) } >
<p>{m.name}</p>
</Link>
I made an implementation that solves this, and it is possible to make any type of custom link component and have it act in any way you like, however it is probably not a good idea for it to wait for a long load event before changing to a route, as users want the route transition to be immediate. The better approach is to use a nice loader and mix it in to the design of your page well, similar to what is done on pitchfork.com and also to ensure data comes back from the API quickly using caching e.g. redis, memcached.
Here is the library I made showing a custom react-router-redux link component: https://github.com/StevenIseki/react-router-redux-link

Prevent deep link in react-router

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.

Resources