Alex Alex - 1 month ago 19
React JSX Question

React router server side not working correctly

I have my app code here: https://github.com/WebTerminator/aldemar/commits/master

and I am trying to get my react app to work on the server side as well, at this stage it works partially.

The problem I have is: (sam problem happens on localhost)

if I navigate within the browser it all works fine, the moment I refresh this URL https://aldemar-productions.herokuapp.com/projects/margam2 I get a console error like:


bundle.js:1 Uncaught SyntaxError: Unexpected token <
enter image description here


If I refresh others URLs like "https://aldemar-productions.herokuapp.com/projects" or "https://aldemar-productions.herokuapp.com/about" they work fine.

server.js

import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './src/client/app/config/routes.jsx';

let port = process.env.PORT || 8080;
let app = express();

app.use(express.static('src/client/'));

// app.get('/', (req, res) => {
// res.sendFile(path.resolve(__dirname + '/src/client/index.html'))
// });

app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {

// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}

// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}

// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(<RouterContext {...renderProps}/>);
}
// else {
// // otherwise we can render a 404 page
// markup = renderToString(<NotFoundPage/>);
// res.status(404);
// }

// render the index template with the embedded React markup
return res.sendFile('index.html', {root : __dirname + '/src/client/'});
}
);
});

app.listen(port);
console.log('server started');


routes.jsx

import React from 'react';
import { Route, Router, browserHistory } from 'react-router';
import ReactDOM from 'react-dom';

import Wrapper from './../components/wrapper.jsx';
import Home from './../components/home.jsx';
import Projects from './../components/projects.jsx';
import SingleProject from './../components/projectContent/singleProject.jsx';
import About from './../components/aboutUs.jsx'

if(typeof window !== 'undefined') {
console.log('here baby');
ReactDOM.render((
<Router history={browserHistory} >
<Route component={Wrapper} >
<Route path="/" component={Home} />
<Route path="projects" component={Projects} />
<Route path="projects/:id" component={SingleProject} />
<Route path="about" component={About} />
</Route>
</Router>
), document.getElementById('app'));
}


singleProject.jsx (where I get the ID parameter from url to load the specific data)

import React from 'react';

import Video from './../video.jsx';
import Overview from './overview.jsx';
import Photography from './photography.jsx';
import Details from './details.jsx';
import Cast from './cast.jsx';

import porgectsCollection from './../../data/projectInfo.js';

import { StickyContainer, Sticky } from 'react-sticky';

class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
mobileMenu: false
};
}
showMobileMenu () {
this.setState({ mobileMenu: !this.state.mobileMenu });
}
render () {
let links = this.props.project.links.map(function(el, i){
return <li key={i}>{el}</li>;
});
const open = this.state.mobileMenu ? ' open' : '';

return (
<Sticky stickyClassName="sticky-nav" topOffset={-100}>
<span onClick={this.showMobileMenu.bind(this)} className="mobile-trigger">X</span>
<nav className={"secondary-nav" + open}>
<ul>
{links}
</ul>
</nav>
</Sticky>
);
}
}

class SingleProject extends React.Component {
getProjectDataFromUrl() {
return **porgectsCollection.filter(el => el.title === this.props.params.id)**;
}
render () {
let data = this.getProjectDataFromUrl(),
project = data[0];
return (
<section className="project-page">
<StickyContainer>
<Video project={project} />
<Nav project={project} />
<Overview project={project} />
<Photography project={project} />
<Details project={project} />
<Cast project={project} />
</StickyContainer>
</section>
);
}
}

export default SingleProject;


When I hit a url like this "https://aldemar-productions.herokuapp.com/projects/margam2" I collect "margam2" as per my routes:

<Route path="projects/:id" component={SingleProject} />


and I load specific data based on that parameter. I believe the problem is around here with doing server side rendering.

UPDATE-1

adding this in the head of my index.html allows to get the content displayed when I refresh the page however the CSS is missing:

<base href="/" />


the css is fully accessible at http://localhost:8080/css/style.css, however when I refresh "http://localhost:8080/projects/margam2" the content is displayed but not the css.

Answer

Ok after 2 days of research, this has helped me:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    **<base href="/" />**
    <link rel="stylesheet" href="./css/foundation.min.css">
    <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
    <link rel="stylesheet" href="./css/font-awesome.min.css">
    <link rel="stylesheet" href="./css/style.css">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
  </head>
  <body>
    <div id="app"></div>
    <script src="./public/bundle.js" type="text/javascript"></script>
  </body>
</html>

however I also found out this in my console:

browser.js:49 Warning: Automatically setting basename using is deprecated and will be removed in the next major release. The semantics of are subtly different from basename. Please pass the basename explicitly in the options to createHistory

so need to find a solution for this.

update-1

even better solution I think. I have changed the assets path form relative to absolute:

see the "/" in front of every local asset

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" href="/css/foundation.min.css">
    <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
    <link rel="stylesheet" href="/css/font-awesome.min.css">
    <link rel="stylesheet" href="/css/style.css">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
  </head>
  <body>
    <div id="app"></div>
    <script src="/public/bundle.js" type="text/javascript"></script>
  </body>
</html>
Comments