window.location.href vs history.pushState - which to use? - javascript

I've been teaching myself react-router, and now I'm wondering which method should be used for going to another page.
According to this post (Programmatically navigate using react router), you can go to another page by this.props.history.push('/some/path').
Honestly, however, I'm not quite sure about the differences between window.location.href and history.pushState.
As far as I understand, window.location.href = "/blah/blah"; leads you to anther page by making a new HTTP call, which refreshes the browser.
On the other hand, what history.pushState (and this.props.history.push('/some/path')) does is to push a state. This, apprently, changes HTTP referrer and consequently updates XMLHttpRequest.
Here is an excerpt from mozila's documentation...
Using history.pushState() changes the referrer that gets used in the HTTP header for XMLHttpRequest objects created after you change the state.
To me, it sounds like both methods make a new HTTP call. If so, what are the differences?
Any advice will be appreciated.
PS
I thought that developers would need to consider whether it's necessary to get data from the server, before deciding how to go to another page.
If you need to retrieve data from the server, window.location.href would
be fine, since you'll make a new HTTP call. However, if you are using <HashRouter>, or you want to avoid refreshing you page for the sake of speed, what would be a good approach?
This question led me to make this post.

history.pushstate does not make a new HTTP call (from mozilla doc quoted by you):
Note that the browser won't attempt to load this URL after a call to pushState(), but it might attempt to load the URL later, for instance after the user restarts the browser.
window.location.href = "url" navigates the browser to new location, so it does make a new http request. Note: exception is the case when you specify new url as hash fragment. Then window is just scrolled to corresponding anchor
You can check both running them from devTools console having Network tab open. Be sure to enable "preserve log" option. Network tab lists all new http requests.

You can test this in the Network of your console. When using history with react router, no refresh is made within the app (no http request). You'd use this for a better UX flow and persistence of state. To my understanding, we're essentially modifying the url (without the http request) and react router uses pattern matching to render components according to this programmatic change.

I use browserHistory in react-router v3. There is no refresh and the application state is maintained throughout.
To redirect, all I do is just browserHistory.push('\componentPath'), componentPath which is mapped in the routes config of the application.
So far had no issues , works like charm.
Hope this help.

Related

Reflect and make Axios request in the browser's URL

What I'm looking for
Bit of JavaScript logic help; although, I am not sure what I will need to do via Vue Router. Not looking exactly for the answer, but more pointed in the right direction (I'm not even sure I'm phrasing this question correctly).
Problem
I have been making API requests with Axios (no problem there). I have a JWT token in the requests’ headers. However, (never done this before) I now need to have the search params the user entered in the browser’s URL when they make a request to share the link to their coworkers which will route to the same page with the same options entered/selected and the API request called with those query params.
What I really don’t know is:
A. What’s the best way to make an API request on route (loading the url with query params)? The route of the page does NOT match the corresponding API endpoint Url.
B. How do I get the users’ entered/selected data into the browser’s URL?
C. Do I need to modify my route objects for those pages to still route correctly even if there is now a query string?
Solution
(Must be purely a frontend solution - I’m using the whole vue ecosystem)
Figured out what I need to do:
A
The project is not server-side rendered; therefore, I need to parse the URL on Vue's created and make a request with the params in the query string. This will seem like the URL caused a API request.
B
Simply append Axios' request query string to the URL, which I believe is cosmetic and should be fine.
C
I'm not sure, but will play around with it.

How to map a custom URL to a REST resource in an SPA?

I have a design issue with my SPA, and hope someone can give me some direction. A user profile page is rendered like this:
The browser fetches /some-username.
The server checks to see if the request was a XMLHTTPRequest or not. It is not, and so it simply returns the bundled javascript app to the browser to execute.
The javascript bundle is executed in the browser, it sees the current URL and makes an AJAX request, again to /some-username.
The server sees the XMLHTTPRequest header, looks up the user who has the custom URL "/some-username" and returns the JSON data about the user back to the javascript to render.
This feels wrong. The app should be making RESTful requests to /users/:id to fetch the user data. But how can it know the id that corresponds to the user with the URL /some-username?
It is worth adding an extra HTTP request just to look up the resource identifier? Something like /get_user_id?url=/some-username.
Are you flexible about your API? If so you may change /some-username to /user-id or if you want to include username /user-id/username but ignore username.
As alternative it is also common to make requests in a filter form. Like /users?username=peter
And feel free to use /users/peter if your username identifies the user. Becuase it's actualy the id (that doesn't have to be integer) and then your url is exactly /users/:id
There is nothing "unRESTful" about /some-username. It's just another resource. The response - I hope - contains the canonical URL /user/id anyway, either as a header or as some kind of "self" link.
That's also how you could achieve your goal. Embed the URL in the page either as JavaScript or as a header equivalent (unfortunately you cannot read the headers of the page request with JavaScript):
//header. Can also use a custom header like X-User-Location
<meta http-eqiv="Location" content="/user/id">
//JavaScript
<script>
var userURL = '/user/id
</script>
I recommend keeping your current approach.

Getting backbone routing to work with pushstate and node.js/express as server

I'm trying to build a single page app with backbone.js on front end and node.js/express as server, I want to have a base HTML file for the root, and then when user navigates to any path such as
mydomain.com/foo/bar
I want to be able to handle that path on the client side by javascript instead of making a round trip to server. I am reading about backbone routing and HTML5 push state. In this article he describes push state like this,
In fact, PushState is really nothing more than a standard API for JavaScript, that allows us to manipulate the browser history by “push”ing full URLs into the browser’s URL without making a round trip to the server, and respond to changes in the URL with Javascript – all without the use of URL hash fragments.
but when I use push state it does actually makes a server request and expects server to deliver contents under /foo/bar . I don't understand how I can avoid that.
Now let's assume that even with push state, your client is going to make a server request under mydomain.com/foo/bar when you visit this URL directly. In that case, since I'm serving the default HTML file, and this default HTML file has links to scripts in it:
<script type="text/javascript" src="/scripts/myscript.js" ></script>
When this HTML loads, it starts looking for scripts under /foo directory instead of root since the server was requested under /foo which obviously does not exist. How do I fix this?
I'm really confused at this point. I'd like to know how URL routing is usually done in a single page application. Any help will be greatly appreciated. You can also refer to this other question I have posted about the same issue: Backbone Router : Get rid of # in the URL
The solution you're trying to implement is very interesting but not that simple. When your server gets a request to mydomain.com/foo/bar, you should redirect to your root with some parameter that the frontend (JavaScript) app can pick-up to know what the original request was for. For example:
Client sends GET http://mydomain.com/foo/bar
Server redirects (responds 302 with Location header set) to http://mydomain.com/#!/foo/bar
Your SPA is loaded in the browser, and on startup you check for the hash and find #!/foo/bar, so you remove the hash and trigger the /foo/bar route (that's a push-state). Your resulting URL is again http://mydomain.com/foo/bar: the original URL the user browsed to.
Grooveshark does something similar to this, though it actually responds with a page to the request sent in 1., which does the hash replacement in the client and then sends another request to the server. It looks unnecessary to me, maybe I'm overlooking something.

AJAX script change navigation on response

I have a simple search application in which I'm trying to use AJAX in order to navigate to different responses.
My problem is pretty straight forward and I'm sure that there is a simple solution to it but I am simply not able to find it.
I'm sending a new request this way:
xhrObj.open('post', 'search.do', false);
xhrObj.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhrObj.send('id=1');
I would like that, after this request call and the necessary business logic, the browser change the view to the new page specified in the back-end.
Instead of this, I'm receiving the page HTML code in the response but the navigation to this page never takes place.
I was under the impression that if the async parameter is set to false the flow would behave the way I was expecting.
How can I make my JS script to jump it's navigation to the a new page after an AJAX call. Please have in mind that I cannot use location.href to jump to the new page because this new page is a .JSP build on the server.
Maybe you should request for a url from the search.do then do this.
xhrObj.onreadystatechange=function() {
if (xhrObj.readyState==4 && xhrObj.status==200)
{
window.location=xhrObj.responseText;
}
}
hope this helps.
If you want to have something similar what guthub has, you need HTML5 History API. Without HTML5 History API you can't manipulate URL (with help JS) without making any redirection.
Generally you could use hash in url (domain/#hash/blabla/bla) but it's not good also.

History.pushState and page refresh

I started looking at HTML5 new history API
However, I have one question. How can one handles the page refresh?
For example a user clicks a link, which is handled by a js function, which
asynchronously loads the content of the page
changes the URL with history.pushState()
The the user refreshes the page, but the URL of course does not exist on the server
How do you handle situations like this? With the hash solution there was no issue there
Thanks
This does require the server-side support. The ideal use of .pushState is to allow URLs from JavaScript applications to be handled intelligently by the server.
This could be either by re-serving the same JavaScript application, and letting it inspect window.location, or by rendering the content at that URL on the server as you would for a normal web application. The specifics obviously vary depending on what you're doing.
You need to perform server side redirection for copy and pasted fake URLs
It all depends on what server side technology you're using. There is no JavaScript way but writing a crazy function in your 404 page that redirect user based on incoming URL that is not a good solution.
The the user refreshes the page, but the URL of course does not exist
on the server
What do you mean by this? I have a feeling your assumption is wrong.
Actually yes, the point is it is the developer who should provide (serverside or clientside) implementation of url-to-pagestate correspondence.
If, once again, I've get the question right.
If you are using ASP.NET MVC on your server, you can add the following to your RegisterRoutes function:
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);
This will send all requests to Index action of your HomeController and your UI will handle the actual route.
For people looking for a JS-only solution, use the popstate event on window. Please see MDN - popstate for full details, but essentially this event is passed the state object you gave to pushState or replaceState. You can access this object like event.state and use it's data to determine what to do, ie show an AJAX portion of a page.
I'm not sure if this was unavailable in 2011 but this is available in all modern browsers (IE10+), now.

Categories

Resources