|project-name
| client
| public
| index.html
| server.js
↑ Project structure
My purpose is to display index.html(in public) in server.js.
[ server.js ]
const express = require('express')
const app = express()
const path = require('path')
app.listen(8080, function() {
console.log('listening on 8080')
})
app.use(express.static(path.join(__dirname, 'client/public')))
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'client/public/index.html'))
})
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/public/index.html'))
})
I wrote the code as above, but when I run node server.js to open the server and connect to localhost:8080, nothing happens.
It seems that the path is not wrong, but I wonder why the React project I made doesn't come out.
[ public > index.html ]
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/public/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="/public/logo192.png" />
<link rel="/manifest" href="/public/manifest.json" />
<title>Project Name</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
[ index.js in client ]
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // 추가됨
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
[ App.js in client ]
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import Main from './pages/Main'
import Login from './pages/Login'
import Register from './pages/Register'
function App() {
return (
<div className='App'>
<div>
<Routes>
<Route path='/' element={<Main />} />
<Route path='/login' element={<Login />} />
<Route path='/register' element={<Register />} />
</Routes>
</div>
</div>
);
};
export default App;
Let me know if you need more code.
Here is the best way to render public HTML files.
Set the view engine like that.
app.set('view engine', 'html');
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res) {
res.render('index');
});
The second option is no need to set the view engine.
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/about.html');
});
Related
I am using React Router. When i try to enter my interface by localhost:3000/Login; i am getting this error. "Invariant failed: Browser history needs a DOM". I googled everywhere but i couldn't find any solutions. I am sharing my codes, anyone to help??
my server.js file
import React from 'react';
import { StaticRouter } from 'react-router-dom';
import express from 'express';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import serialize from 'serialize-javascript';
import App from '$/containers/app';
import setupStore from '$/state/store';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', (req, res) => {
// Compile an initial state
const preloadedState = { counter: 0 };
// Create a new Redux store instance
const store = setupStore(preloadedState);
const context = {};
// Render the component to a string
const markup = renderToString(
<Provider store={store}>
<StaticRouter context={context} location={req.url}>
<App />
</StaticRouter>
</Provider>,
);
// Grab the initial state from our Redux store
const finalState = store.getState();
if (context.url) {
res.redirect(context.url);
} else {
res.status(200).send(
`<!doctype html>
<html lang="">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="utf-8" />
<title>Welcome to Razzle</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
${
assets.client.css
? `<link rel="stylesheet" href="${assets.client.css}">`
: ''
}
${
process.env.NODE_ENV === 'production'
? `<script src="${assets.client.js}" defer></script>`
: `<script src="${assets.client.js}" defer crossorigin></script>`
}
</head>
<body>
<div id="root">${markup}</div>
<script>
window.__PRELOADED_STATE__ = ${serialize(finalState)}
</script>
</body>
</html>`,
);
}
});
export default server;
my app.js file
import React from 'react';
import Home from '$/components/pages/home';
import Login from '$/components/pages/login';
import Logout from '$/components/pages/logout';
import '$/css/app.css';
const { Route, Switch, BrowserRouter } = require('react-router-dom');
const App = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/logout" component={Logout} />
</Switch>
</BrowserRouter>
);
export default App;
my client.js file
import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import setupStore from '$/state/store';
import App from '$/containers/app';
const store = setupStore(window.__PRELOADED_STATE__);
const { BrowserRouter } = require('react-router-dom');
hydrate(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById('root'),
);
if (module.hot) {
module.hot.accept('./containers/app', () => {
hydrate(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
});
}
When running my application on the localhost I had no problem visiting urls that I manually typed in or refreshing the page but after using heroku to deploy the app, I can only visit pages by navigating through the home page. After reading some posts on here( React-router urls don't work when refreshing or writing manually ) , I decided to try to apply a "catch-all" solution. I have implemented the /* function in the 'server.js' file but the webpage loads the blank index.html page instead of the correct react component and can not figure out why. The code for the related pages are below:
Api.js
`
const request = require('request');
const express = require('express');
const app = express();
const router = express.Router();
var varViews = 0;
const {createServer} = require('http');
const path = require('path');
const PORT = process.env.PORT || 3000
const dev = app.get('env') !== 'production'
if(!dev){
console.log("Not Dev mode");
app.use(express.static(path.resolve(__dirname, 'build')));
app.use(express.static(__dirname + '/public'));
app.get("/", (req, res) => {
console.log('Home page');
res.send("This is the home page");
})
app.post("/usbstat", (req, res) => {
varViews++;
var views = {views: varViews};
res.json(views);
})
app.delete("/usbstat", (req, res) => {
varViews = 0;
var views = {views: 0};
res.json(views);
})
app.get("/usbstat", (req, res) => {
var numViews = {views: varViews};
return res.json(numViews);
})
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, '/public/index.html'), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
}
app.listen(PORT, () => {
console.log('App is listening on'+PORT);
})
module.exports = app;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Nova Cyber Security Test</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script src="../src/index.js"></script>
</body>
</html>
app.js
import React, { Component } from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import history from './history';
import Home from './pages/home/Home';
import USBDrive from './pages/usbdrive/USBDrive';
import phishingStats from './pages/phishingStats/phishingStats';
import Admin from './pages/admin/Admin';
class App extends Component {
render() {
return (
<Router history={history}>
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/USBStats' component={USBDrive}/>
<Route exact path='/phishingStats' component={phishingStats}/>
<Route exact path='/admin' component={Admin}/>
</Switch>
</Router>
);
}
}
export default App;
I have a react server-side rendering application along with node & express js.
Routes.js
import React from 'react';
import { Route } from 'react-router-dom';
import Home from './Home';
import Test from './Test';
export default () => {
return (
<div>
<Route exact path="/" component={Home} />
<Route exact path="/test/:deviceId" component={Test} />
</div>
);
};
index.js
import express from 'express';
import renderer from './helpers/renderer';
const app = express();
app.use(express.static('public')));
app.get('*', (req, res) => {
console.log(req.url);
res.send(renderer(req));
});
app.listen(3090, () => {
console.log('listening at http://localhost:3090');
})
renderer.js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import Routes from '../components/Routes';
export default req => {
const content = renderToString(
<StaticRouter location={req.path} context={{}}>
<Routes />
</StaticRouter>
);
return `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app">${content}</div>
<script src="bundle.js"></script>
</body>
</html>
`;
};
client.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import Routes from './Routes';
ReactDOM.hydrate(
<BrowserRouter>
<Routes />
</BrowserRouter>
, document.getElementById('app'));
Well, I am trying to pass a deviceId along with my route /test/:deviceId,
and my HTML template under renderer.js looks for bundle.js file under /test/build.js instead of /bundle.js
How do I make sure my bundle file always points to the correct location which is under public folder exposed by
app.use(express.static('public'));
if I visit, / route to Home component, it looks for bundle.js under correct folder which is the public folder.
Do let me know if you need any extra information.
Well, finally I have figured out what was changing the path of bundle.js with each out.
if my route is '/', it will look for bundle.js at http://localhost:3090/bundle.js
if the route is '/test/:deviceId', it will look for bundle.js at http://localhost:3090/test/:deviceId/bundle.js (in this scenario it will never find bundle.js)
I fixed it my modifying the html temlate under renderer.js
for the tag I changed path to bundle.js from 'bundle.js' to '/bundle.js'
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import Routes from '../components/Routes';
export default req => {
const content = renderToString(
<StaticRouter location={req.path} context={{}}>
<Routes />
</StaticRouter>
);
return `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app">${content}</div>
<script src="/bundle.js"></script>
</body>
</html>
`;
};
Now, it will always look for bundle.js under http://localhost:3090/bundle.js
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 <
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.
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>
On your project's page, you are using an inline close div, like this:
<div id="app" />
In all your other pages, you are using a empty div
<div id="app" > </div>
And that's the right way to do it.
React-Router do not re-render your page, it only "re-add" your react code to your id="app".
That's why when you open clicking it works and when you refresh it broke.
I have a React app that also uses Redux and ReactRouter.
My problem is the following:
When I launch the app and go to the root url, I can normally navigate inside the app, and the routes in the navigation bar will be changing as I navigate.
However, if I type in the navigation bar any url other than the root, I get a weird error:
I don't really get how one can get such an error.
If I go to localhost:1337/ and then click on the item with the link /cars/1, everything will be fine and the component will successfully get rendered. If I type localhost:1337/cars/1 right away (or any other existing route) I get this error.
Here's how I initialize the react-router and define my routes:
index.js:
require('./style/style.css');
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 reduxPromise from 'redux-promise';
import routes from './routes';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(
reduxPromise
)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container.app')
);
routes.js:
import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';
import App from './components/app';
import CarsIndex from './containers/cars-index';
import CarNew from './containers/car-new';
import CarShow from './containers/car-show';
import CarEdit from './containers/car-edit';
import SignIn from './containers/signin';
import auth from './auth/auth';
function requireAuth(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/authenticate',
state: { nextPathname: nextState.location.pathname }
});
}
}
function filterLoggedIn(nextState, replace) {
if (auth.loggedIn()) {
replace({
pathname: '/',
state: { nextPathname: nextState.location.pathname }
});
}
}
export default (
<Route path='/' component={App}>
<IndexRoute component={CarsIndex} onEnter={requireAuth} />
<Route path='cars/new' component={CarNew} onEnter={requireAuth} />
<Route path='cars/:id' component={CarShow} onEnter={requireAuth} />
<Route path='cars/edit/:id' component={CarEdit} onEnter={requireAuth} />
<Route path='authenticate' component={SignIn} onEnter={filterLoggedIn} />
<Redirect from='*' to='/' />
</Route>
);
My server is a small express.js app and it redirects any requests other than /api/* to the index.html page.
Here's a part from my server.js:
const path = require('path');
const port = process.env.PORT || 1337;
const app = express();
const pathToStatic = path.join(__dirname, 'static');
express.static(path.join(__dirname, 'static'));
app.use(express.static(pathToStatic));
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.get('/api/cars', (req, res) => {
return Car.find((err, cars) => {
if (!err) {
return res.send(cars);
} else {
console.log(err);
res.statusCode = 500;
return res.send({ error: 'Server error' });
}
});
});
app.get('*', (req, res) => {
res.sendFile(path.resolve(pathToStatic, 'index.html'));
});
app.listen(port, () => {
console.log(`Express server is listening on port ${port}`);
});
Have you encountered such an issue? Could you help me to find out the soultion to this problem?
The screenshot shows that bundle.js (when requested) is returning index.html.
This is because your express routes handle route api/cars and then default everything else to index.html.
Of course all your resources that appear on index.html must also be sent the browser. This includes <script src="bundle.js"></script> which the browser will request once it gets index.html the first time.
So, you must have some way to allow express to handle requests for the resources that index.html needs.
A popular solution to this is to mount an assets directory and place that above the default route. Something like:
// api routes
app.use(express.static('assets'));
// default route
Then make sure your bundle.js is inside the assets directory. And then that the script tag looks like <script src="/assets/bundle.js"></script>.