Listen for client side router (react router) to finish loading a new route in a chrome extension? - google-chrome-extension

I am building a chrome extension that adds features to an existing website that uses react router. I am able to handle the initial load but every subsequent load is difficult since there doesn't appear to be an event I can listen to for when the new route has finished loading. Currently, I am using a service worker to message the content script when the URL of the tab changes but that doesn't account for the time it takes to load the new page. The crucial part about this is that I am not the owner of the site I am modifying so I can't add anything to the code to make this easier, I'm just working with what's given to me.
I am currently using a MutationObserver on the react router root component to check for the page load following the URL change but it is clunky and inconsistent since it doesn't work for all routes. Is there a better way to go about this? Is there an event I can listen for or a specific quirk of the router that I can use to make this snappier? I am basically just looking to create my own callback function for when the router finishes loading the new page.

Related

Refreshing with Browser History using React Router returns calls from my API

From what I understand, to use the browserHistory object in React Router properly, we need to use a wildcard * route so that the user is returned the single page application upon every refresh. Where this becomes a problem is when my client-side URL is the same as one of my api URLs.
For example, say I have a website called example.com and that one of the pages on this site is example.com/profile. If I refresh, the expected behavior is that it will simply reload the website example.com and route correctly to /profile, since the wildcard route should have returned the client the single page application. Where this doesn't work is if I have an API route /profile, i.e. example.com/profile. Now when the user refreshes the page, instead of being returned the the profile page, they are instead returned a JSON response from my API.
Depending on the order in which the wildcard route is declared, I can either have the API response returned on refresh, or the single page app on refresh, but not both when the API url is the same as the client side browser url.
Are there any suggestions around this issue? My current solution is to migrate all of my API routes to have /api/ prepended to them.
You need to make sure your client routes do not collide with your API routes, and prefixing API routes with /api is a pretty standard way to handle this.
There's no real way "around" the issue other than making sure they don't collide – various workarounds might be possible, but it's going to be more clear for everyone involved if routes are unambiguous.

Server side response to allow client side routing

I am developing a single page application that has a client side router. so although the base url to run the application will be either http:://example.com or http:://example.com/index.html - skipping the domain name that is routes '/' and '/index.html'
But somewhere in my application, because of my client side router, I may call up a route something like '/appointments/20160113 and the client router will redirect me to the appropriate "Appointments Page" inside my SPA passing the parameter of todays date.
But if the user calls directly http://example.com/appointments/20160113, I am led to believe that the server should respond directly with /index.html so the browser doesn't get a 404.
I am building the server using nodejs (specifically the http2 module, but I don't think that is very important, and my examples don't use https, although with http2 they do). I just tried changing the server so if its hit with an unknown url it responds with the index.html file.
However, the browser sees this as its first response and then makes requests for the rest of its attached files relative to the url (so for instance follows up with /appointments/20160113/styles/main.css). This leads to an infinite loop, as the server responds with another copy in index.html (and immediately gets a request back for /appointments/20160113/styles/styles/main.css ).
Its too early in the lifecycle of the page for the javascript to be running yet (and specifically the router software), so clearly the approach is too simplistic.
I have also written the client side router (using the history api) so I can change things if I need to.
How should this situation be handled. I am thinking perhaps a 301 redirect to /index.html or something and then the router's initial dispatch knows this and can do a popstate or something. I ideally want to support the passing of urls via external means between users, but until I actually tried to implement it I hadn't realise the implications.
I don't know if this is the best way or not, but having not received any answers on here, I decided to try a number of different ways and see which worked out the best.
Each way involved doing a 301 redirect to /index.html, and then providing the url from which I was redirecting via different mechanisms
This is what I tried
Setting a cookie with a short expiry date the value of which was the url
Adding a query string with a ?redirect= parameter with the url
Adding a #fragment after /index.html with the url
In the end I rejected 1) because chrome wasn't deleting the cookie after I had used it and making the value shorted lived depends on accurate timing between client and server. The solution appeared too fragile.
I tried 2) and it was looking good until I came to test it. Unfortunately setting window.location.search causes a page reload, and I was really struggling with finding out what was happening. However, what I discovered in 3) about mocking could well be provided to a solution based on 2) so it is one that could be used. I still might return to this solution as it "feels" right to me.
I tried 3) and it worked quite well. However I was struggling with timing issues in testing since my router element was using the #fragment during initialisation, but I couldn't set the window.location.hash until after the router was established in the test suite. I wanted to mock window.location.hash with sinon so I could control it, but it turns out you can't
The solution to this was for the router to wrap its own calls to window.location.hash in a library, so that I could mock the library. And that is what I did in the end and it all worked.
I could go back to using a query string and wrapping window.location.search in a library call, so I could stub that call and avoid the problems of page reloading.

Cannot request Iron Router server route twice without refreshing the client

I am calling a meteor method, which generates a file using fs. I wait for a callback giving me the path where the file exists, and then I request the file with a server route. The code is very similar to this SO answer. I have also tried using createReadStream (as demonstrated here) instead of passing the file directly to response.write.
This all works well on the client's first click of my export/download button. However, if for some reason they want to click the button more than once, the file will get generated but the file will not get served by Iron Router. There are no errors on the client or server. If the user refreshes the client, then the feature will work again (once).
Why do I need to refresh the browser in order to request the same server route a second time? Am I doing something wrong?
Example Application
Does the URL change when they click the first download? If so, and the second route is the same, you will not get redirected as you are already there. If this is the case, can you use the router hooks to send the user back to the route they came from?

angular.js/backbone.js without changing the address bar

It seems that angular.js and backbone.js both prefer the user to change the browser state (via the address bar) and then they will re-act regarding the defined routes. That is still a single-page web application, but in my case, I really don't need to change the address bar when user is interacting with the application and I prefer to avoid it as much as possible also.
As a newbie in both angular.js and backbone.js, I'm facing difficulty on how to structure my single-page web application while using either angular.js or backbone.js, but without touching the address bar.
I have a node.js, jade and socket.io setup (without express.js), and the socket.io is the main way of communication between client and server.
To be more specific, imagine that I'm loading the dashbaord.jade, now how can I inject some partials into the main markup when socket.io detects an event? Can I replace the entire dashbaord.jade also - the already rendered markup?
So what's the problem? Don't change the url! don't use router and make your app just interact with user by mouse (or keyboard) events or just update model or views every single time socket send event!!
My solution : don't create any router, implement new class to use socket and by any event of socket do appropriate action. In that class define a method for each socket event, that loads template and model (maybe return data from socketIO event) and render view by those data.

Express & Backbone Integration

Ok, I am new to web dev and here's a stupid question. I have been through a few tutorials for node, express and backbone individually, but I can't seem to wrap my head around how they are integrated. Particularly, consider this use case:
Person X opens the browser, types in a URL and hits enter->Express responds to the request and sends some data back to the browser.
My question is, where does backbone come into the picture here ? I know it's a mvc framework to organize your JS code. But, I can't find a place in this use-case where the server/browser interacts with backbone. Only thing I can think of is that the backbone saving the route and serving the page the next time. But what about the first time ? It would be best if someone could explain to me how the request gets routed from client browser to express/backbone to browser again.
Also, am I correct in assuming response.send() or response.json() will send the result to backbone when model.fetch() is called ? I mean, is there no additional code required ? Being new to web dev, I'm quite not used to the idea of the framework 'taking care' of everything once you send the response back.
EDIT : Here's what I have understood so far. Feel free to correct me if I am wrong. When I access websites like gmail, the server first sends a big html file including backbone.js code in it. The backbone.js code listens for events like clicking on links in the html file and handles them if the links are defined in it routes(routes are always relative to current route, accessing a completely different route sends request to the server). So, if I click compose, my url remains the same because backbone handles the request. However, if I click Maps/News services in the bar above, the server handles the request.
There is no special integration between backbone and node.js.
If you use the standard backbone sync method then all you need to do is:
Use the static middleware in express to serve up your static html/js/... files.
Define RESTfule routes in express that conform to what backbone is expecting.
Backbone does indeed make an http call when you do model.fetch. You could look in Chome network tab to see where it's sending the request to and then implement that route in express.

Resources