Refreshing site with parameters doesn't work in React.js - reactjs

When I try to refresh the site when the url have a parameter I get a blank page.
My routes.js file is as follows :
const routes = (
<Route path="/" component={App}>
<IndexRoute component={HomePage}/>
<Route path="registration/:id" component={hideIfLoggedIn(Registration)}/>
<Route path="registration" component={hideIfLoggedIn(Registration)}/>
<Route path="reset-password" component={PasswordReset} />
<Route path="portal" component={requireAuth(UserPage)} />
</Route>
);
When I'm on http://localhost:8001/registration everything works perfect. If I refresh the page it works without problems.
But the problem is when I'm on http://localhost:8001/registration/step-one . First time when it loads it works good, but when I try to refresh the site I get a blank page.
EDIT
gulp.task('connect', ['watch'], function () {
connect.server({
root: ['dist'],
port: config.port,
base: config.devBaseUrl,
livereload: true,
fallback: 'dist/index.html'
})
});

You have to configure your server to serve index html for any other route .
Browser request /registration/step-one , it does not found index.html at this path hence fails
Use connect-history-api-fallback.
The middleware is available through NPM and can easily be added.
npm install --save connect-history-api-fallback.
Import the library
var history = require('connect-history-api-fallback');
Now you only need to add the middleware to your application like so
var connect = require('connect');
var app = connect()
.use(history())
.listen(3000);
Of course you can also use this piece of middleware with express:
var express = require('express');
var app = express();
app.use(history());

Related

Routes are not valid when using basename property of Router on production mode

I'm using this Router:
import {BrowserRouter as Router, Route, Switch } from "react-router-dom";
<Router basename={process.env.REACT_APP_ROUTER_BASE || '/MyApp'}>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/login" component={Login} />
{/*<Redirect path="*" to="/" />*/}
</Switch>
</Router>
When working with dev mode (npm start), it's working and redirecting as expected.
When working with production mode (npm build --> copy the output of build to WebContent folder on a war to be deployed on tomcat), it behaves differently:
http://host:port/MyApp is working fine, and routing using the app is fine too
routing manually by typing the route value on the browser, e.g.
http://host:port/MyApp/login is giving: Page can't be found.
(Note: typing the route manually in dev mode working fine)
Why it differs? How it can be solved?
What I had tried:
Reference: React routing not working while manually changing URL | React-router 4
adding devServer entry (with historyApiFallback: true) to my webpack.config.prod.json:
export default {
resolve: {
extensions: ['*', '.js', '.jsx', '.json'],
// To support react-hot-loader
alias: {
'react-dom': '#hot-loader/react-dom'
}
},
devtool: 'source-map', // more info:https://webpack.js.org/guides/production/#source-mapping and https://webpack.js.org/configuration/devtool/,
devServer: {
historyApiFallback: true,
},
What historyApiFallback: true does is it renders index.html if the requested file doesn't exist. Your production webserver is not doing this, so when it attempts to load /MyApp/login, it see that there is no file called /MyApp/login (or /MyApp/login/index.html depending on configuration) and returns a 404.
Since React Router is handling your routing logic client side, you need to set up your webserver to fallback to delivering index.html if it can't find a page.

Client side routing with react-router and expressJS only works one level deep

I'm trying to use react-router to enable client side routing, but I can only get the routing to behave as expected at one level deep past "/". (i.e. localhost:8080/ works, localhost:8080/{id} works, but localhost:8080/vote/{id} does not)
Additionally, I am trying to develop locally using webpack-dev-server, and deploy to heroku using webpack -p and an expressjs server. My express server is set to have all routes default to index.html.
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, './dist/index.html'));
});
When using npm start (express server) and I try to navigate to localhost:8080/vote/{id} the console says: SyntaxError: expected expression, got '<' meaning I have a situation like this issue. However, when using webpack-dev-server, I get a different error in the console that says: Loading failed for the with source “http://localhost:8080/vote/bundle.js”. I believe what I'm seeing is two different outputs for the same core problem, the difference being my environment or differences in how express/webpack-dev-server are serving up the content.
Here is my full expressJS server:
const express = require('express');
const path = require('path');
const port = process.env.PORT || 8080;
app = express();
// the __dirname is the current directory from where the script is running
app.use('/', express.static(path.resolve(__dirname, 'dist')));
// send the user to index html page inspite of the url
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, './dist/index.html'));
});
app.listen(port);
console.log("server started on port " + port);
Here are the relevant parts of my webpack.config.js:
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: "/"
},
devServer: {
contentBase: path.resolve(__dirname, "dist"),
historyApiFallback: true
}
And here is my App.js with relevant routes (Home, RealtimeView, PollVote being custom React components):
export default class App extends Component {
render() {
return (
<BrowserRouter history={browserHistory}>
<div>
<Route exact path="/" component={Home} />
<Route exact path="/:id" component={RealtimeView} />
<Route exact path="/vote/:id" component={PollVote}/>
</div>
</BrowserRouter>
);
}
}
With this configuration, I can reliably get localhost:8080/ to work and localhost:8080/{whatever} to work but localhost:8080/vote/{id} or any route more complicated than localhost:8080/{something} fails with the errors I mentioned earlier depending on if I'm using webpack-dev-server or my expressjs server.
FWIW I'm relatively new to webdev (my experience so far is that it's a cluster-fck) and I can't go with a totally isomorphic app because my back-end is in java/spring and I'm not re-writing my whole back-end. I found this post to be helpful but it doesnt solve my problem. Please help this is making me crazy.
Try setting fallback file explicitly:
historyApiFallback:{
index: 'index.html'
}

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!

React-Router Broken after changing from hashLocation to browserLocation

I can't figure out why after changing from hashLocation to now using browserLocation that after I am able to click the link below to navigate to (render the interview) the interview, but now for some reason once I'm at the interview (I am redirected to the interview component that renders), if I refresh my browser and try to hit interviews/companies/:companyId again when I'm on that same page already, it instead hits my page not found in my express.js implementation.
So again, summarizing this: first time around when I click the link from my main landing page which is where that <Link> resides..when i first load the website, when I click that...it's able to hit interviews/companies/:companyId and render the interview component. All is good, until after you try to hit refresh, it bombs out. Not sure why
server.js
'use strict';
var express = require('express'),
app = module.exports = express(),
port = process.env.PORT || 3000,
path = require('path');
app.use(express.static('client'));
app.use(function (req, res) {
res.send('Sorry, Page Not Found');
});
console.log("port we're about to run on: " + port);
app.listen(port, function () {
console.log('Ready on port %d', port);
}).on('error', function (err) {
console.log(err);
});
On my main landing page, I click a link that's defined like this in one of my React Components:
<Link to={`/interviews/companies/${company.id}`}
params={{id: company.id}}
className="ft-company"
ref="link">
{company.name}
</Link>
which initially works fine. I am sent to /interviews/companies/6 for example and it renders my interview component just fine
Here's my route definitions:
const App = Component({
render() {
return (
<Router history={browserHistory} onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/">
<IndexRoute component={HomePage}/>
<Route name="interview" path="interviews/companies/:companyId" component={Interview}/>
</Route>
<Route path="/" component={Container}></Route>
</Router>
);
}
})
You need to add entry in your webserver to serve the index.html for every get html request .
Import the library:
var history = require('connect-history-api-fallback');
Now you only need to add the middleware to your application like so:
var connect = require('connect');
var app = connect()
.use(history())
.listen(3000);
Of course you can also use this piece of middleware with express:
var express = require('express');
var app = express();
app.use(history());
https://github.com/bripkens/connect-history-api-fallback

React-router "Cannot GET /*" except for root url

I am willing to use React-router for my application, and I am trying the example given in the doc first, which I copied below. Now when I go to localhost:3000/, I see "App" as expected, but every other page, such as localhost:3000/inbox returns "Cannot GET /inbox". What am I missing here ?
var About = React.createClass({
render: function () {
return <h2>About</h2>;
}});
var Inbox = React.createClass({
render: function () {
return <h2>Inbox</h2>;
}});
var App = React.createClass({
render () {
return (
<div><h1>App</h1>
<RouteHandler/>
</div>
)}});
var routes = (
<Route path='/' handler={App}>
<Route path="about" handler={About}/>
<Route path="inbox" handler={Inbox}/>
</Route>
);
If you are using webpack-dev-server there is an option called history-api-fallback. If set to true 404s will fallback to /index.html.
Add the option to devServer section of the Webpack config like this:
devServer: {
contentBase: 'app/ui/www',
devtool: 'eval',
hot: true,
inline: true,
port: 3000,
outputPath: buildPath,
historyApiFallback: true,
},
Link to Webpack docs: https://webpack.js.org/configuration/dev-server/
webpack-dev-server docs on Github: https://github.com/webpack/webpack-dev-server
I believe the issue is that you are making a http resource request:
GET /inbox HTTP/1.1
Host: localhost:3000
but are using client-side only routing. Are you intending on doing server side rendering too? You might need to change your router location to be HistoryLocation instead of HashLocation (the default).
The location prop of Router.run tells it where to match the routes against. If you're not running server side React, I believe you have to use Router.HashLocation (or just leave it blank).
If not, you are accessing your component the wrong way. Try using http://localhost:3000/#/inbox. It can take a little to familiarize yourself with React-Router but it is definitely worth it!
React Router Documentation - HashLocation
Simplest option: use hash history instead. The urls stay are not very nice and if you need better SEO, I'd opt for server-side rendering as well.
If you're interested in detail about this, this answer was really helpful for me: React-router urls don't work when refreshing or writting manually
The request will be sent to the server, and the directory/file does not really exists so it will return 404, so you have to tell the server to return the index page for all requests, and react will handle the rooting :
if like me, you are hosting your react app on IIS, just add a web.config file containing :
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
This will tell IIS server to return the main page to the client instead of 404 error.
I came across the same problem, if you have a server that responds to requests and a HashRouter if you are using a static file server. Instead of using BrowserRouter use HashRouter its not the perfect fix, but should solve the cannot GET "/path" error. be sure to import HashRouter from 'react-router-dom'
render() {
return (
<div>
<HashRouter>
<Blog />
</HashRouter>
</div>
);
}
source: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/guides/basic-components.md
To add to those not helped by the answer: historyApiFallback: true works only for the first level of the url. So site.com/about will be redirected to index but site.com/about/me won't be. In this case, you need to add the rewrite option:
historyApiFallback: {
rewrites: [
{ from: /./, to: '/index.html' }
]
}
This will match using a Regex expression all routes. Take care to also add this:
output: {
// ...
publicPath: '/'
//...
},
Otherwise, the redirect will work to index.html, but index.html may have just bundle.js as the webpacked script, and will not read correctly. It should instead be '/bundle.js', which this config option will do.
I had the same problem. Finely worked with webpack-dev-server, but not with express.
The problem was in version of Express.js. I haven't noticed that it was beta (5.0.0-beta.1)
The solution is upgrading / downgrading to stable version (4.18.2 works fine).
Hope it helps!

Resources