Render a component from outside ReactJS - node.js

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.

Related

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.

Checksum Invalid - SSR props to Client

I'm using the react engine react-engine on GitHub to create a node, express app with react for the views.
For the most part, my app is rendered on the server. However, on one page/express route I require the view to be rendered server-side and then for the React to be fully interactive on the client.
So far I've got the view rendering server-side and then being re-loaded/re-mounted by React on the client.
My problem is that I'm now getting the following error:
bundle.js:357 Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) <section data-reactroot="" data-reactid
(server) <section cl
Here's what my code looks like:
class FormCreate extends React.Component {
render() {
return (
<ReactBlank title="Create new application form" messages={this.props.messages} authUser={this.props.authUser}>
<script dangerouslySetInnerHTML={{
__html: 'window.PROPS=' + JSON.stringify(this.props)
}} />
<div id="app-content">
<Main {...this.props}/>
</div>
</ReactBlank>
);
}
}
FormCreate.propTypes = {
messages: React.PropTypes.array,
authUser: React.PropTypes.object,
form: React.PropTypes.object
};
module.exports = FormCreate;
The above is initially rendered on the server and then the following re-mounts it on the client:
var React = require('react');
var ReactDOM = require('react-dom');
var Main = require('./app/views/shared/builder/Main.jsx');
document.addEventListener('DOMContentLoaded', function onLoad() {
const propScript = document.getElementById('react-engine-props');
const props = window.PROPS;
ReactDOM.render(
<Main {...props} />,
document.getElementById('app-content')
);
});
Can anyone see a problem here?

How can i access a nodeJS variable in ejs views in a sails project?

My LoginController.js looks like this:
module.exports = {
getAuthorizationLink: function (req, res) {
res.send({
authorization_link: sails.config.FACEBOOK_LOGIN_URL
})
}
}
I need to redirect to the authorization_link when a button is clicked
<div class="col-md-4 text-center">
<button id="authenticate" class="btn btn-primary">Authenticate Page</button>
<script type="text/javascript">
document.getElementById("authenticate").onclick = function () {
...
};
</script>
</div>
Here you are looking to mix server-side (EJS) & client-side JS code.
It is possible, makes sense to do sometimes but it is not clean.
Once you understand you are doing this. Variable can be passed and accessed.
Using EJS, write JS code for client side e.g.
var auth_link = '<%= authorization_link %>';
this line will become something like below for client-side JS
var auth_link = 'https://fb.com/login';
Now you can use auth_link in client-side JS as required
Also, check res.view for responding with HTML page

rendering react on server

I've been trying to figure out how to render react on the server (node/express) and finally found a simple enough tutorial to understand what's going on. But now, after setting everything up, I'm getting an error in the React.render method:
here's my component file:
var React = require('react');
var box = React.createClass({
render: function() {
return (
<div style='padding: 10px'>
this.props.text
</div>
);
}
});
React.render(<box text='testing server side'/>, document.body);
module.exports = box;
I get an error when I run npm start:
document is not defined
how do I get around this? do I need or not need the render method?
to give more context, this box component is being required by another component:
var React = require('react');
var Box = require('../react-jsx/box.js'); //this is the box component
var Component = React.createClass({
render: function() {
return (
<html>
<head>
<title>
React Server Rendering
</title>
</head>
<body>
<Box text='testing'/>
<script src="public/bundle.js"></script>
</body>
</html>
);
}
});
module.exports = Component;
and this is all being used in index.js
require('node-jsx').install();
var React = require('react');
var Component = require('../custom-modules/test-react-server-module.js');
var express = require('express');
var router = express.Router();
router.get('/react', function(req, res, next) {
var markup = React.renderToString(Component());
res.send(markup);
});
module.exports = router;
and if I remove the render method I get this error in the browser:
Cannot read property '__reactAutoBindMap' of undefined
I saw some people saying that may be due to the jsx transformer being old, but I think I have the latest version
I'm out of ideas
First of all, I'd recommend update your React version. The current version exposes two different Top-Level API's: React, which is generally used to create components and ReactDOM, which exposes DOM-specific methods to be used at the top level of your app.
There is to things to point out here:
You are trying to run an code that is supposed to be executed only at the browser. There is no document in NodeJS. I'd suggest using webpack to pack this component files and serve them on browser.
For an isomorphic React application, you need to have a client.js file that calls the render function for the same component you are trying to render inside index.js. Got it?
Understand the ReactDOM.render, as the documentation states:
Render a ReactElement into the DOM in the supplied container and return a reference to the component (or returns null for stateless components).
If the ReactElement was previously rendered into container, this will
perform an update on it and only mutate the DOM as necessary to
reflect the latest React component.
Keep in mind, again, that ReactDOM.render should be only used a few times and generally at the top level of your app, just one time.
Having said this, your box.js should look like:
var React = require('react');
var box = React.createClass({
render: function() {
return (
<div style='padding: 10px'>
this.props.text
</div>
);
}
});
module.exports = box;
For this to properly work, you will need to create a main component main-component-file.js:
var React = require('react');
var Box = require('../react-jsx/box.js'); //this is the box component
var Component = React.createClass({
render: function() {
return (
<html>
<head>
<title>
React Server Rendering
</title>
</head>
<body>
<Box text='testing'/>
<script src="public/bundle.js"></script>
</body>
</html>
);
}
});
module.exports = Component;
Inside your bundle.js you need to make sure that this is being called so the main component tries to re-render:
var React = require('react');
var ReactDOM = require('react-dom');
var Component = require('main-component-file.js');
ReactDOM.render(<Component/>, document.body);
At last but not least: index.js, the server file. Change React.renderToString to ReactDOMServer.renderToString, create a React Element from your main component and use it:
var element = React.createElement(Component)
router.get('/react', function(req, res, next) {
var markup = ReactDOMServer.renderToString(element);
res.send(markup);
});
Don't forget to include the npm package react-dom/server at your index.js.
References used in the article:
React.createElement
ReactDOM.render() docs
ReactDOMServer docs
You should delete this
React.render(<box text='testing server side'/>, document.body);
There is no need for it. What you are essentially telling React to do is render it right then and there.
document.body doesn't exist yet because you are rendering it server-side and you also don't need it because you render the component in the renderToString function upon request in the router. (Also I think #PeterLyons is correct so take a look at his answer too).
Also if you are strictly using React for views only, you might want to take a look at express-react-views. They have a good tutorial of how to use React with Express and you essentially can use it for server-side rendering only. I don't think it's as ideal as using react-router depending on what you're building but it illustrates how React is handling the server-side rendering and works with Express.
You need to use the react-dom/server package's renderToString function in the server environment. This will return the HTML as a string which you can send in your express response.

React Components on the Server

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>

Resources