React Components on the Server - node.js

I've been playing around with React for a while now but still can't wrap my head around on how to integrate it with an existing node/express/handlebars app.
For example, if I had a feed component which required json data to be fetched from AWS - how do I handle this.
var VideoFeed = require('./component/VideoFeed');
app.use('/', function(res, req) {
DataService.getVideoFeed().then(function(data) {
res.render('home', {videoComponent: React.renderToString(<VideoFeed feed={data} />);
});
});
Home
<!DOCTYPE html>
<body>
Some sample text. Here's the Video Feed
{{videoComponent}}
</body>
</html>

Related

How to return a 404 Not found page in an Express App?

I have an express app, in which I have the following code:
app.get('*', (req, res) => {
res.send('404', {
title: 404,
name: 'James Olaleye',
errorMessage: 'Page not found',
});
});
However, My IDE is warning about this message:
express deprecated res.send(status, body): Use
res.status(status).send(body) instead
And with the above code, My Browser is returning the following payload as a JSON object:
{
"title": 404,
"name": "James Olaleye",
"errorMessage": "Page not found"
}
What I want, is to display a 404 Not found page to the user, how can this be achived?
You have two seperate problem
1: you are using an old way to response to the request insted use this res.status(STATUS_CODE).send(BODY)
2: you are sending a json yet you want to display a 404 page in this case you need to send a html template
so your code should look like this
app.get('*', (req, res) => {
res.status(404).send("<div>404 Not Found</div>");
});
I updated your question a bit to make it clearer for future references.
the method res.send is deprecated, among other things because it's usages is too ambiguous. A server response, can be a lot of things, it can be a page, it can be a file, and it can be a simple JSON object (which you have here).
In your case, when you run res.send(404,{ /*...*/ }), the express app assumes you want to send a JSON object, so it does just that.
There are multiple possible ways, to achieve what you want, but I will stick to the most simple solution.
If you want to display an HTML page, in the most simplest form, you can actually just change your piece of code to do this instead:
app.status(404).send(`<h1>Page not found</h1>`)
This will essentially, show a page, instead of a JSON object.
You can even define the whole HTML file if you like:
app.status(404).send(
`
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
`
)
This would be the fastest way to achieve what you want.
A step further, would be to create an HTML file some where in your app, and to send the HTML file instead.
If your source code looks like this:
/
src/
index.js
htmls/
404.html
<!-- htmls/404.html -->
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
// src/index.js
const express = require('express');
const app = express();
const path = require('path');
const PORT = 3000;
app.get('/', function(req, res) {
const options = {
root: path.join(__dirname, '..', 'htmls')
};
res.sendFile('404.html', options, function (err) {
if (err) {
next(err);
} else {
console.log('Sent:', fileName);
}
});
});
This would allow you to have multiple HTML files which you can send around.
There are like I stated, other options as well, but that would make this answer way too long and out of scope. If you are interested, you can research Using template engines with Express and start with the following link.
Happy coding :)

Ejs doesn't load when I call a function from another file

Basicly I want to run a function when I clicked the button, but it works when I started the server and go to localhost one time, here's what's supposed to happen, after that localhost page doesn't load. (Unable to connect error)
If I remove the function there is no problem. How can I get it to work only when I click the button ?
Many thanks.
My func.js
const mongoose = require("mongoose");
const axios = require('axios');
async function func() {
//MyCodes
}
module.exports = {
func: func
}
My index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button type="button" onClick= <%= func.func() %> >Click</button>
//Other codes are independent the button
</body>
</html>
My res.render codeblocks in app.js
var func = require('./func');
app.get("/", (req, res) => {
res.render('index', {
cName: name,
symbol: symbol,
len: finds[0].result.length,
cPrice: price,
cDate: date,
func:func
});
});
});
})
You are misunderstanding. You cannot call an internal nodejs function(backend) from the html (frontend). If your frontend need to execute some backend operation like query to mongo, you have these options:
#1 client side rendering (Modern)
This is the most used in the modern world: Ajax & Api
your backend exposes a rest endpoints like /products/search who recieve a json and return another json
this endpoints should be consumed with javascript on some js file of your frontend:
html
<html lang="en">
<head>
<script src="./controller.js"></script>
</head>
<body>
<button type="button" onClick="search();" >Click</button>
</body>
</html>
controller.js
function search(){
$.ajax({
url:"./api/products/search",
type:"POST",
data:JSON.stringify(fooObject),
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(response){
...
}
})
}
Note 1: controller.js contains javascript for browser not for backend : nodejs
Note 2: ejs is only used to return the the initial html, so it is better to use another frameworks like:react, angular, vue
#2 server side rendering (Legacies)
In this case, ajax and js for browser are not strictly required.
Any event on your html should use <form> to trigger an entire page reload
You backend receives any parameter from the , make some operations like mongo queries and returns html instead json, using res.render in your case
Note
Ejs is for SSR = server side rendering, so add ajax could be complex for novices. In this case, use the option #2
You cannot use a nodejs function (javascript for server) in the client side (javascript for browser). Maybe some workaround are able to do that but, don't mix different things.

Why res.send( string ) download a file

I have an app where I want to render static html with express.
I know about res.sendFile() but that is not what I want to use. I want to load my files using fs and send them using res.send()
I do something like this :
fileFoo = fs.readFileSync('./html/foo.html', 'utf-8');
fileBar = fs.readFileSync('./html/bar.html', 'utf-8');
app = express()
app.get('/foo', (req, res) => res.send(fileFoo));
app.get('/foo/bar', (req, res) => res.send(fileBar));
With 2 very simple html files
foo.html
<html>
<head>
<title>FOO</title>
</head>
<body>
<h1>FOO</h1>
bar
</body>
</html>
bar.html
<html>
<head>
<title>BAR</title>
</head>
<body>
<h1>BAR</h1>
</body>
</html>
When I go to /foo I got a HTML rendered.
If I click on bar I do not get a new HTML page rendered but I got a file "bar" with no extension downloaded. The file of course content the HTML page.
What is happening ?
UPDATE :
I am actually using a router, not direct app.
And I think this 'download' occure only when I try to reach
router.get('/', (res, req) => res.send(fileBar));

Creating HapiJs server that can serve complex web pages

I have just started learning Node.js and hapi.js. What I am trying to accomplish now is build a REST web server that should also have a web interface for configuration and statistics collection.
I found that Inert plugin allows serving static pages and, as I understand, this limits me to loading a single web page that consists of a single file.
However, what I do not understand is whether it is possible to setup hapi.js to serve a full dynamic webpage with css, js and other files referenced within its body.
Am I heading the wrong direction with this or else how can I setup my scenario?
You can serve multiple static files from a specified directory.
Just tried out inert with hapi, using this example:
https://github.com/hapijs/inert#static-file-server
Inert has no problem serving multiple static files from a given directory, e.g public.
So you'll have no issue serving multiple static html, css, js files from a specified dir. You can then build a dynamic JSON api using Hapi, and have that consumed by your js client-side code, served from static js files in your public dir.
If you are needing templating, where you generate dynamic content on the serverside, hapi can do that out of the box, check out:
http://hapijs.com/tutorials/views
Sorry if this isn't what you mean, do feel free to clarify if not :-)
Hope that helps!
The vision plugin is used for templating, which is what I think you're after. If you want to bundle css and js files along with your pages, you can put them in a public directory and serve that with the inert plugin. And then you only need to reference the relative path in whatever html file you're trying to render.
Here's a simple example that uses handlebars. Inert is responsible for serving your css and js files while vision still renders your templates.
./index.js
var hapi = require('hapi');
var server = new hapi.Server();
server.connection({port: 5555});
server.register([require('vision'), require('inert')], (err) => {
if(err){
throw err;
}
server.views({
engines: {
html: require('handlebars')
},
relativeTo: __dirname + '/',
path: 'www'
});
var homeroute = {
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.view('index', {name: 'cuthbert'});
}
};
var publicassetsroute = {
method: 'GET',
path: '/public/{param*}',
handler: {
directory: {
path: './public',
listing: false,
index: false
}
}
};
server.route(homeroute);
server.route(publicassetsroute);
server.start((err) => {
console.log('server started -- ' + server.info.uri)
});
});
www/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hapi Test</title>
<link rel="stylesheet" href="../public/index.css">
</head>
<body>
<h1>A Hapi Happy Test.</h1>
<p>This is a test page. Woo!</p>
<p>My name is {{name}}.</p>
</body>
</html>
public/index.css
p {
color: blue;
}

Render a component from outside ReactJS

From here :
"The only way to get a handle to a React Component instance outside of React is by storing the return value of React.render."
I need to render a React component outside React and the reason for it I'm going to mention below.
In my node.js, expressJS app, I am using 'react-router-component' and 'react-async'.
In app.js -the file which is supposed to be run ,
var url=require('url');
var App=require('./react/App.jsx');
var app = express();
app.get('*',function(req,res){
//}); SEE EDIT 1 BELOW
var path = url.parse(req.url).pathname;
ReactAsync.renderComponentToStringWithAsyncState(App({path:path}),function(err, markup) {
res.send('<!DOCTYPE html>'+markup);
});
});
In App.jsx,
PostList = require('./components/PostList.jsx');
var App = React.createClass({
render: function() {
return (
<html>
<head lang="en">
</head>
<body>
<div id="main">
<Locations path={this.props.path}>
<Location path="/" handler={PostList} />
<Location path="/admin" handler={Admin} />
</Locations>
<script type="text/javascript" src="/scripts/react/bundle.js"></script>
<script type="text/javascript" src="/scripts/custom.js"></script>
</body>
</html>
});
bundle.js is the browserified file from all the .jsx files.
In PostList.jsx,
var PostList = React.createClass({
mixins: [ReactAsync.Mixin],
getInitialStateAsync: function(cb) {
if(typeof this.props.prods==='undefined'){
request.get('http://localhost:8000/api/cats_default', function(response) {
cb(null, {items_show:response.body});
});
}
},
setTopmostParentState: function(items_show){
this.setState({
items_show:items_show
});
},
render: function() {
return (
<div className="postList" id="postList">
**// Things go here**
<div className="click_me" >Click me</div>
</div>
}
});
PostListRender=function(cart_prods){
var renderNow=function(){
//return <PostList cart_prods={cart_prods}></PostList>
React.renderComponent(<PostList cart_prods={cart_prods}></PostList>,document.getElementById('postList') );
};
return {
renderNow:renderNow
}
};
module.exports=PostList;
In custom.js:
$('.click_me').click(function(){
PostListRenderObj=PostListRender(products_cart_found);
PostListRenderObj.renderNow();
$('odometer').html('price');// price variable is calculated anyhow
});
The page shows well.
EDIT 3 Starts
Now I want to render the PostList component on clicking the click_me div .
EDIT 3 Ends
But when I click on the click_me element, the browser shows script busy, console shows
ReactJS - ReactMount: Root element has been removed from its original container. New container
And the Firebug log limit exceeds.
So why I want to render on click from outside react.js:
I have to run the jQuery Odomoeter plugin on clicking the click_me div. The plugin was not developed as a node middleware although it can be installed the way a middleware is installed and the plugin codebase is saved inside node_modules folder.
Edit2 Starts:
As the plugin is not a node middleware, I cannot require it from inside node. However I can perform the click event (code not shown ) from inside node and run the following code there as well :
$('odometer').html('price');// price variable is calculated anyhow
In this case I include the plugin in the browser with <script /> tag and the browserified bundle.js comes after the plugin script . But the animation is not properly shown. So I take to the client side click event in the custom.js.
If I do not require the plugin to be a middleware from inside node
and just include it in the page before the browserified JS file and
perform the click event inside React, then the odometer animation is
not properly shown.
Edit2 Ends:
So what is the way to render the PostList React component outside React ?
EDIT 1 The }); was quite mistakenly placed there
I cannot understand your question description, but this answers the title question:
How you render React components outside of react?
MyComponent = require('MyComponent')
element = document.getElementById('postList');
renderedComp = ReactDOM.render(MyComponent,{...someProps},element);
// => render returns the component instance.
$(document).on('something, function(){
renderedComp.setState({thingClicked: true})
})
Inside of react you can just call the component.

Resources