React Router / Express - Refreshing dynamic URL parameters - javascript

My React router works fine when navigating to '/Article/1/' via a link, though when I refresh my browser, it no longer detects my Article component.
Root.js
import React from 'react';
import { BrowserRouter as Router, Route, Link, browserHistory } from 'react-router-dom';
import Home from '../page/Home/Index';
import Article from '../page/Article/Index';
const Root = () => {
return (
<Router history={browserHistory}>
<div>
<ul>
<li><Link to={'/'}>Home</Link></li>
<li><Link to={'/About'}>About</Link></li>
</ul>
<hr />
<Route exact path={'/'} component={Home} />
<Route path={'/Article/:id'} component={Article} />
</div>
</Router>
);
};
export default Root;
Server.js
var express = require('express');
var app = express();
app.use('/', express.static(__dirname + '/dist'));
app.listen(9000, function() {
console.log('listening on 9000.')
});
I read online that it may be related to me not having a wildcard in my server.js - could anyone point me in the right direction? thank you in advance!
EDIT - this is what I tried (still not rendering):
app.use(express.static(__dirname + '/dist'))
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname + '/dist/index.html'));
});

I read online that it may be related to me not having a wildcard in my server.js
Yep, that'll be it. The goal here is to have your client handle the routing and not the server, for that to work you need the server to return the client app regardless of what URL it gets.
Simple fix - change / to /* on your server route.

Related

MERN router links dont work when manually typing in URL

I deployed my MERN stack app to heroku but the router paths dont work when i manually type in the url paths.
This is my node server:
const express = require('express');
const path = require('path');
const connectDB = require('./config/db');
const app = express();
// //connect DB
connectDB();
app.use('/api/poll', require('./routes/api/poll'));
if(process.env.NODE_ENV === 'production'){
app.use(express.static( 'client/build' ));
app.get('*', () => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html')); //relative path
});
}
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started ${PORT}`));
This is the router in react:
<Router>
<Fragment>
<Navbar />
<Switch>
<Route exact path="/" component={Landing} />
<Route exact path="/create" component={CreatePoll} />
<Route exact path="/search" component={Search} />
<Route exact path="/404" component={NoMatch} />
<Route exact path="/:id" component={Poll} />
<Route exact path="/:id/results" component={Results} />
</Switch>
<NotificationContainer />
</Fragment>
</Router>
Basically if I type in a pathname in my browser url, for example, baseUrl/poll/vote, it doesnt work. Only if I type in the link it does that. If I get to that path by my app redirects, then it works.
Router needs to be an instance of HashRouter, not BrowserRouter.
import {BrowserRouter as Router} from 'react-router';
BrowserRouter needs special configuration and consideration on the backend to handle network requests to routes that, in fact, should be handled by the frontend.
HashRouter is easier to use because it inserts a # into the URL, effectively sending all page requests to the same base route without the server needing to do anything special.

React router open page using URL

I am new in react programming. Trying to solve issue my self but, stuck on following issue.
I have following react router code.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
class Main extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/' component={Content} />
<Route path='/user/:id' component={User} />
<Route path='*' component={NotFound} />
</Switch>
</Router>
);
}
export default Main
In content there are list of users with their photo. If i click on person photo it will redirect me to particular user.
I wrote my code like:
<Link to={'/user/' + userItem.id}>
<img className="useritem-img" src={userItem.photo} alt={userItem.tagline}/>
</Link>
It will open User Component properly with new URL like: http://localhost:3000/user/457365 on photo click.
But, when copy and paste same url in new tab it will not open. May be i am wrong some where.
Any help would be greatly appreciated.
Edit:
I am getting following error when i open that page:
Cannot GET /user/457365
I am not using create-react-app just simple react application.
Following is my server.js
app.use(express.static('dist'));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, './index.html'));
});
app.listen(port, function (err) {
if (err) {
console.log(err);
} else {
open('http://localhost:' + port);
}
})
If you are getting that error that means that the server is trying to handle the routing. Therefore you should make sure that the server allows the SPA to handle the routing.
For instance, if you are using express, you probably want to do something like this:
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('/api/whatever', (req,res) => {
// Whatever your api does
});
// Allow the SPA to take care of the routing
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});

React-Router Nested Routes not working

Steps to reproduce
client.js (entry file)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxThunk from 'redux-thunk';
import reducers from './reducers';
import routes from './routes.js';
const storeWithMiddleware = applyMiddleware(reduxThunk)(createStore);
const store = storeWithMiddleware(reducers);
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} routes={routes} />
</Provider>, document.getElementById('app')
);
routes.js (ver 1)
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/bases/app.js';
import Login from './components/app/authentication/login.js';
export default(
<Route path="/" component={App}>
<Route path="signup" component={Login}/>
</Route>
)
routes.js (ver 2)
let routes = {
path: '/',
component: App,
childRoutes: [
{ path: 'signup', component: Login }
]
}
export default routes;
Expected Behavior
Expect to have /signup route avail.
Actual Behavior
react-router cannot find the route /signup but can find /
Having a look at the chrome dev-tools source-tab, this is what I find:
When looking at "/"
sources
--------
dist/prod
| bundle.js
index.html
When looking at "/signup"
sources
--------
signup
If you changed to hashHistory and it worked it probably be your backend which serves the html...
Since hashHistory works like this:
example.com/#/signup
The browser doesn't understand as a new GET, if you use browseHistory, this:
example.com/signup
Makes the browser request for index.html again but on path /signup ... but the webpack dev server probably don't understand..
Try adding historyApiFallback: true to webpack config
LIke this
https://github.com/amacneil/react-router-webpack-history-example
The giveaway is the files that are being served when you are looking at the sources. When you are trying to load the /signup page, your browser is trying to load a signup page.
When you use browserHistory, you need to serve your index.html (and any scripts included in it) for all possible routes. This means that you need to have a server which accepts all possible routes and responds accordingly.
For example, if you are running a node server using express, you would need to have a wildcard route handler:
// define static handler first for .js, etc.
app.use(express.static(path.join(__dirname, 'public')));
// all other routes should server your index.html file
app.get("/", handleRender);
app.get("*", handleRender);
function handleRender(req, res){
res.sendFile(__dirname + '/index.html');
}

React router base url not working

I've got this piece of code as a router for my React 15.0.1 universal app. React router version is 2.3.0.
import React from 'react';
import { createHistory } from 'history';
import { Router, Route, IndexRoute, Redirect, useRouterHistory } from 'react-router';
import MainLayout from './../views/layout/mainLayout.jsx';
import Home from './../views/home.jsx';
import About from './../views/about.jsx';
import Error404 from './../views/404.jsx';
const browserHistory = useRouterHistory(createHistory)({
basename: '/example-url'
});
module.exports = (
<Router history={browserHistory}>
<Route path='/' component={MainLayout}>
<IndexRoute component={Home} />
<Route path='about/' component={About} />
<Route path='*' component={Error404} />
</Route>
</Router>
The problem is, that I'm getting the following error:
Invariant Violation: Browser history needs a DOM
It seems that this solution doesn't work for my server side routing, as I'm trying to set a basename for my routes.
If I forget about the basename, then everything is fine but my application doesn't behave like a spa (I believe that routes on client side are not found).
Here is some of my server side entry point (app.js):
import routes from './client/routers/routes.jsx';
const engine = ReactEngine.server.create({
routes: routes,
routesFilePath: path.join(__dirname, 'client/routers/routes.jsx'),
});
app.engine('.jsx', engine);
app.set('views', __dirname + '/client/views');
app.set('view engine', 'jsx');
app.set('view', ReactEngine.expressView);
app.get('*', function(req, res) {
res.render(req.url, { metadata: res.data.metadata,baseUrl: res.data.baseUrl, url: res.data.url });
});
Could anybody help please? I have read through the docs and supposedly this is the right way to do it and many people seem to support the solution but I'm stuck here.
Thank you very much
what's your history version?
if your react-router version is v2/v3, history version should be v3, v4 is for react-router v4

Refreshing page gives Cannot GET /page_url in react-router 2

I am using client side rendering with react 0.14 and react-router 2. I have deployed my app to a local node server.
I am at url (server_url/component1). Whenenver I refresh the page I am getting
Cannot GET /component1
error from server side.
I know this is happening because I am sending request to server again for /component1 which route does not exists on server. But I want to disable that whenever I am refreshing the page it should handled by client side routing only. I do not want to send any request to server.
Entry point to my app index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Router,browserHistory} from 'react-router';
import routes from './routes';
ReactDOM.render(
<Router routes={routes} history={browserHistory}/>
, document.querySelector('.init')
);
My routes.js file
module.exports = (
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="/component1" component={comp1}/>
</Route>
)
Edit:
In this scenario should I use browser history or hash history? Are they same?
If I understood you correctly, then you have to redirect all your requests, that don't match defined routes, to the frontend. If it is just a static html file, the route should look like:
app.get('*', function(req, res) {
res.sendFile('public/index.html');
});
UPDATE
To let other routes work, you should put them just in front of catching route, since express applies them vice versa (the first defined route will be applied as the last one):
app.get('/any_route_here', (req, res) => {
res.json({msg: 'ROUTE WORKS'})
});
app.get('*', (req, res) => {
res.sendFile('public/index.html'); //pas routing to react
});
In such a way, if you hit a request like: yourserver.host/any_route_here you will get a json message ROUTE WORKS, but on any other request, the routing will be passed to your frontend.

Categories