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'));
});
Related
I currently have a website built using EJS and using express on the backend on port 3300. The structure of the routes look like this:
localhost:3300
-/movies
-/rating
-/review
-/tvshows
-/rating
-/review
I am currently returning EJS files in the routes like this:
router.get("/:title/rating", function(req, res) {
Movie.find({ movieName: req.params.title })
.exec(function(err, foundMovie) {
if (err) {
console.log(err);
} else {
console.log(foundMovie)
res.render("movie/rating", { movie: foundMovie});
}
});
});
But now, I want to add a new route in the structure that uses React such that the following will be built using React:
localhost:3300
-/documentary
-/rating
-/review
From my understanding, if I want to use React, I would have to re-write all my previous routes (that returns EJS) as React components since you can't merge the two servers (React and Express as they both run on different ports: 3000 and 3300 respectively). But since I have so much written already I tried to render the new routes on the serverside by following this tutorial resulting in:
router.get("/documentary", (req,res) => {
Documentary.find({}).exec(function(err,foundDoc){
if (err){
console.log(err);
} else {
fs.readFile(path.resolve("./views/react/client/build/index.html"), "utf-8", (err, data) => {
if (err) {
return res.status(500).send("Error");
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
);
});
}
})
});
And App.js looking like:
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/rating">
<h1>Rating page for Documentaries</h1>
</Route>
<Route exact path="/review">
<h1>Review page for Documentaries</h1>
</Route>
</Switch>
</BrowserRouter>
);
}
I get an error:
Error: Invariant failed: Browser history needs a DOM at invariant
How do I fix the error? Or is there a better way to combine both EJS routes and create new React routes? Thanks in advance
Issue
You need to use a different Router in the server as well.
Since BrowserRouter in the client React app cannot be processed in the server.
Static Router is what you're looking for.
Solution
Server-side rendering in React is very tricky. I recommend you to use Nextjs which makes it easy.
For side-projects, refer to this blog
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.
I am using an express server to serve a create-react-app build.
server.js
// Express only serves static assets in production
if (process.env.NODE_ENV === 'production') {
server.use(express.static('client/build')); // <--- this might be causing my problem, how to make work?
}
var api = require('./routes/api');
var email = require('./routes/email');
// Define API routes before the *
server.use('/api', api);
server.use('/email', email);
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
app.js
class App extends Component {
render() {
return (
<Router history={browserHistory}>
<Route component={Container}>
<Route path="/" component={Main} />
<Route path="/catalog" component={Catalog} />
<Route path="/product/:productId" component={ProductPage} />
<Route path="/faq" component={Faq} />
<Route path="/blog" component={Blog} />
<Route path="/inquiry" component={Inquiry} />
<Route path="/cart" component={Cart} />
</Route>
</Router>
);
}
}
export default App;
Edit:
Updated my server.js file to show relevant code. I think the issue lies in using express.static('client/build'), but I'm not quite sure what to change it to in order to fix my routing issues.
/Edit
This works perfectly fine in Dev and Heroku where clicking links in the app go from route to route. For example being at the root, I can click on a link to "https://thesite.com/catalog" and the app navigates there as with any other route.
However, if I were to copy the URL above and paste it into a new tab/browser, I get a 500 Internal Server Error. This would mean the user cannot bookmark any page other than the root. Or if another site linked to a specific route on my page it would show a 500 error. Refreshing in a page that isn't "/" returns a 500 error. Does anyone know how to fix this?
Figured it out. I needed to add a universal route getter for express.static('client/build').
if (process.env.NODE_ENV === 'production') {
server.use(express.static('client/build'));
server.use('*', express.static('client/build')); // Added this
}
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.
I have this simple code that doesn't work. I took it from the react-router project page and modified slightly for it to look a bit better.
Setup
I have several very simple react components:
var IndexPage = React.createClass({
render(){
return (<h1>Index Page</h1>);
}
});
var AboutPage = React.createClass({
render(){
return (<h1>About page</h1>);
}
});
var NotFoundPage = React.createClass({
render(){
return (<h1>Not found page</h1>);
}
});
Also I have made a setup of react router:
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var Route = ReactRouter.Route;
var BrowserHistory = ReactRouter.browserHistory;
var Render = ReactDOM.render;
And that's how I use react router 2.0.
Render((
<Router history={BrowserHistory}>
<Route path="/" component={IndexPage}>
<Route path="about" component={AboutPage}/>
<Route path="*" component={NotFoundPage}/>
</Route>
</Router>
), document.body)
I use BrowserHistory (not HashHistory) to avoid hash in urls.
My server is raised under IIS 10 (Windows 10) on 8080 port.
Problem
http://localhost:8080/
goes to IndexPage component. This is correct.
http://localhost:8080/about
goes to IIS 404 error page. Routing doesn't work in this case
http://localhost:8080/ttt
goes to IIS 404 error page again.
So the router doesn't see this nested paths that go after /. And it doesn't even care about whether they are correct or not.
What can cause such a strange behavior?
Thanks in advance!
Update
I've found out that the next string solves the problem with a client routing:
{this.props.children}
This is the fixed code:
var IndexPage = React.createClass({
render(){
console.log('index page render');
return (<div>
<h1>Index Page</h1>
<Link to={ '/about' }>about</Link>
{this.props.children}
</div>);
}
});
Is your server configured to map all application-paths to your index.html? You should never get a IIS 404 if your server would map all path to your index file, as it always would deliver this one.
React-router is a client-side routing solution(also works in server side, but it seems you are using .Net as the server side handler).
You can try this(Link from React-Router):
<Link to={ pathname: 'about' }>
about
</Link>
Put it in the index page's component, and click the link, the react-router will work.
Update:
{this.props.children} ref to the inner component(AboutPage, NotFoundPage).
If you change code to that:
<Route path="/" component={IndexPage} />
<Route path="/about" component={AboutPage}/>
<Route path="*" component={NotFoundPage}/>
It will works.(remove the {this.props.children} in IndexPage if there is no nesting routes).
I was able to solve my issue linking directly to URLs by adding --history-api-fallback to my webpack-dev-server command.
Setting this flag prompts webpack-dev-server to serve the index page instead of 404. The index page will then route correctly on the client side.