React router behaviour with google cached pages. - reactjs

We have recently ported our application from monolithic RubyOnRails to React/Redux Framework.
We are facing a problem with react-router and google cached pages in the results of google. In this case we have a SPA(single page application), which uses react-router (via browserHistory). The problem here is that: google cached page is a page wrapper, where the URL differs by the URL defined in the router of the SPA. So, in this case the routing of the application falls to the definition of a page not found. And the cached result of SPA page by google, instead showing the content of the page, page is reloaded to /search (wrapper path).
React Router Version: 3.0.3
React Version: 15.4.2
Here is how we have handled the generic Urls:
<Route onEnter={hitTheServer}>
<Route path="*" component={Home} />
</Route>
hitTheServer definition:
const hitTheServer = (nextState, replace, cb) => {
const url = `https://${config.server.public.host}${nextState.location.pathname}`;
// Hit the server only on client side rendering. A hack to handle unimplemented pages
if (typeof window !== 'undefined') {
window.location = url;
} else {
cb();
}
};
Here, we are doing page reload because there are some of the links are still not implement by out SPA, so, on full page reload, these links are router to their respective server (rails or node) via nginx.

Related

Asp.net core web application with react template: in development, server checks mvc routes first, but in prodcution the server only returns index.html

I am trying to migrate an existing MVC application that uses razor pages into web API + ReactJS client. I'm doing this step by step which means I have migrated the homepage (Home/Index) plus a few other pages/features to react. There are links between razor pages and those that are implemented in react. This works perfectly in development environment which runs react development server.
For example in development environment, if I route to localhost:12345/Controller/Action (for an available Controller and action) server executes the respective action and returns the proper view and if I try to access a route which is unknown to server it returns index.html and react-router handles the routing from this point. This is the accepted behavior and works like a charm in the development environment.
But things change when I build the react app and run the application in production mode. The request never reaches the server and is handled on the client-side by react-router. When I hard-refresh the page it triggers the MVC actions as accepted though.
I would like to have the same behaviour it has in development for the production.
This is how the starup.cs file looks like.
// ConfigureServices
.
.
.
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ReactSource/build";
});
.
.
.
// Configure
.
.
.
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ReactSource";
// do not start the react development server in production
if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); }
});
.
.
Any Idea what I'm missing here? Should I modify the react-router for this?
I solved my problem by turning off (unregistering) the create-react-app out of the box service worker which tried to cache assets and was preventing the requests to reach the server.
In the index.js file, I simply imported { unregister } from "./registerServiceWorker" and replaced registerServiceWorker(); with unregister(); at the end of the auto generated index.js file.
To have a Progress Web Application, you must enable the Service worker in your React web app.
For solve cache problem in production mode you must enter following code in index.tsx file:
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<App />
</BrowserRouter>,
rootElement);
//index page is Route exact path.
if (window.location.href.includes('index'))
registerServiceWorker();
else
unregisterAndReload();
function unregisterAndReload() {
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
}
and App.tsx file should be like the following:
render() {
return (
<Layout>
<Route exact path='/index' component={Home} />
<Route path='/contact' component={ContactUs} />
</Layout>
);
}

Host react app and laravel backend(Admin panel) in same laravel application project

I want to host the react app and laravel app in the same laravel application project.
The front end app is react and backend(admin panel) laravel.
I want redirect all request to specific front end view except first URL segment == backend/:any
Eg.
http://host.com/backend/(any)
Continue with laravel router
http://host.com/(any) except backend/
Continue with react router
Any idea for that matter?
You have two options here, either pass a regular expression to the any route to ignore API prefixed routes
Route::get('/{any}', function () {
return view('index.blade.php');
})->where('any', '^(?!backend).*$');
Route::fallback(function () {
return view('index.blade.php');
});
From the docs
Fallback Routes
Using the Route::fallback method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you may define the fallback route within your routes/web.php file, all middleware in the web middleware group will apply to the route. You are free to add additional middleware to this route as needed:
Route::fallback(function () {
//
});
The fallback route should always be the last route registered by your application.
Try something like this in the "routes/web.php":
// WRITE BACK-END ROUTES AT FIRST
Route::group([
'prefix' => 'backend',
], function () {
Route::get('/', 'AdminController#dashboard')->name('dashboard');
Route::get('admin-page-1', 'AdminController#page1')->name('page1');
Route::get('admin-page-2', 'AdminController#page2')->name('page2');
// some other admin routes if you need
});
// FRONT ROUTE(S)
Route::get('/{text}', 'FrontController#front')->name('front');

Exclude API/login urls from router routing

I have a React App which proxies requests to Django, and Django has allauth installed to manage social app login. But my app is SPA which uses react-router, so when I have a link like /accounts/github/login, it seems like it is only passed to router, not attempred to open correctly. How can I exempt some url mask from routing?
To redirect a route (page-URL) of your SPA to another page-URL -out of your SPA- use this:
<Route path='/login' component={() => window.location =
'https://example.com/somethingOutOfSPA'}/>
*works with router v3,v4

React Router cannot GET /route only after deployed to Heroku

I have a simple web-app made with create-react-app and express.
All of the pages made with react router work fine locally, as well as online on my own machine once deployed to Heroku.
But, after testing online on other machines, I can't access these pages - whenever I click the links to them it displays Cannot GET /*route*
I still have the *name*.herokuapp.com domain if that affects it in any way
The redirect code I use is as follows: (I use firebase and react-bootstrap as well)
class App extends Component {
render() {
return (
<div>
<MyNavbar/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/eateries" component={Eateries}/>
<Route exact path="/thank-you" component={ThankYou}/>
</Switch>
</div>
);
}
Redirecting to /thank-you:
componentWillMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
window.location = "thank-you"
}
})
}
So essentially when a user signs in through a modal component it should take them to /thank-you
Redirecting to /eateries:
<NavItem href="/eateries">
For Eateries
</NavItem>
Is there something wrong with the way I'm redirecting users or using react router in general?
It's hard to know without seeing your server code - but in order to support react-router's rendering mechanism, you need to use a wild card route in your server code:
app.get('*', (req, res) => res.sendFile(path.resolve('build', 'index.html'));
This basically means "for any route not already matched, send the index.html file", which will then load your webapp, which in turn will handle routing. Note that you need to add the static middleware serving your assets before this - that's a gotcha I've forgotten many times. Most of your server file would then look like this:
const express = require('express');
const app = express();
app.use(express.static('build'));
app.get('*', (req, res) => res.sendFile(path.resolve('build', 'index.html'));
app.listen(process.env.PORT, () => console.log('listening for connections'));
Now, this would seem to work either way locally, since your web app is already loaded, and handles routing for you.
However, I've noticed that you're using window.location when redirecting your user. This makes some browsers at least (probably all) request the new page from the server, instead of letting the app deal with it. Instead, use the provided history property, which contains a push method.
componentWillMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.props.history.push('/thank-you');
}
});
}
This adds a new entry to the history stack. If you want a regular redirect, you should use .replace instead.
Hope this helps!

Refreshing a react router page that has a dynamic url/params

I have several components displayed with react router that have dynamic url paths. An example path I have is
<Route path="/newproject/:id" onEnter={checkSesh} component= {ProjectDetails} />
When entering this component, I have a componentWillMount function that extract the id part of the url so that I can get the info for the correct project and render it on the ProjectDetails component.
componentWillMount() {
var id = this.props.router.params.id
this.props.teamDetails(id);
}
this.props.teamDetails(id) this calls a redux action creator that will make an axios request to an express route that will get the project info from the database.
export function teamDetails(id) {
return function(dispatch) {
axios.get('/getteaminfo/' + id)
.then(res => {
dispatch({ type: "SET_TEAM_DETAILS", payload: {
teamInfo: res.data.teamInfo,
admin: res.data.admin,
adminID: res.data.teamInfo.teamAdmin,
teamMembers: res.data.teamInfo.teamMembers
}
})
});
}
}
everything works fine upon visiting the page after already being logged in etc. But when I refresh the page /newproject/:id, i get an error Uncaught SyntaxError: Unexpected token <. An example url in my browser looks like http://localhost:3000/newproject/58df1ae6aabc4916206fdaae. When I refresh this page, I get that error. The error is complaining about my <!DOCTYPE html> tag at the very top of my index.html for some reason. This index.html is where all of React is being rendered.
When page is refreshed store state is not preserved. Make sure the state is not important to load the page, or at least initialized properly every time.
For e.g. login information if saved in store and not on browser with localStorage or cookies etc.. then on refresh, the error will come when trying to access /getteaminfo/ route through axios. The response will have error html and it can't be parsed by js.
Please check your web console on for more information. You can use chrome extension like https://github.com/zalmoxisus/redux-devtools-extension which will show your store and etc..
Make sure to check what /getteaminfo/ gives with id is not passed.
Also, make sure on your server side, did you route requests to react-router path through something like this?
e.g. express js,
app.get('*', function response(req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
be sure to sendFile with the real location of index.html
I found the answer here: react-router dynamic segments crash when accessed I added <base href="/" /> into the <head>of my index.html. You can also read more info here: Unexpected token < error in react router component

Resources