I'm building isomorphic app using Node.js and ReactJS.
I've met a situation when inside some isomorphic React component (rendered both on client and server sides) I need to include only client-side dependency (some notifications library for example).
What is the best way to have it worked (compiled) both on server/client sides?
P.S: I have few thoughts about possible workarounds something like checking for browser env variables (ex. some typeof window !== 'undefined') etc. but I'm not sure it's a good way to go.
Use the lifecycle method componentDidMount which is not invoked on the server instead of checking if window is undefined.
The "downside" is if your client side dependency enhances an element for example or changes any property of it, it'll get the default property first since it was rendered server side and when componentDidMount runs it'll get changed causing a "blink".
If you are using browserify I often use process.browser which is only true in browserified code.
If you wanted to get fancy and remove server code from the browser instead there is also isbrowser which will do just that.
Another way (webpack or browserify) is to take advantage of the browser field in the package.json. You can make it so that the server requires a noop file and the browser requires a file the exposes the client side api.
I have defined variable in webpack configuration file called process.env.WEBPACK and in my code when i need something like bottstrap js or something else i just write
if(process.env.WEBPACK){
//awesome lib included or scss or whatever
}
Related
I would like to expose a node.js server variable to the client. I am trying to get express-expose to work.
I am not sure how to initialise and use that library.
In the express-expose guide the usage doesn't make sense
var express = require('express');
var expose = require('express-expose');
app = expose(app);
app.expose(...);
how can I use app in 'expose(app') before it was initialised ?
I used
const app = expose(express());
app.expose('var some = "variable";','head');
but that doesn't seem to work either.
Can anyone supply me please with an example that initialises the express-expose library and exports var 'some' to the client ?
(By exposing to the client I mean it will be available to all my javascript files as a global variable so that I could do 'console.log(some);' and it would print its value)
Using Pug
Since you're using Express with Pug, you can just call the res.render function and give it the variable you want. Here's how to do that.
Let's say you have a template called index.pug, it may look like this:
html
head
title= title
body
h1= message
On your server, the code responsible for the rendering should look like this. In this case we're passing someVariable to the view that needs to be rendered.
const someVariable = 'hello world!';
app.get('/', function (req, res) {
res.render('index', { title: 'Hey', message: someVariable })
})
A note on express-expose
The express-expose library is not maintained and hasn't been updated in 5 years. My advice would be to just render your variable as described previously or just use AJAX requests.
A note on using a global variable
I am using the 'pug' view engine. It is passed to the view but I want
it as a global javascript variable.
A better idea is to use Template Inheritence to create "generic layouts" and extend them as needed. Sharing a global variable with file you're rendering will not update it magically on the front-end if you're changing it in the back-end. Even if the variable is global.
If you want to reflect "real-time changes" you should look into Socket.io.
This seems like a code smell... But regardless, there are cleaner ways to do it.
If you want to expose a data object, that is, an object just containing keys and values, you could embed it in the rendered webpage, make it available via some API endpoint that the client can acquire via an AJAX request, or expose a raw Javascript file that can be included via <script> (or I guess script(...) in pug).
If you want to expose a more complex Javascript object, such as a class or an object with function definitions, you could expose the Javascript file and include it via script(...).
However, you should be very careful exposing a file used by the server. If there are any vulnerabilities, they are now public. If there are any hard-coded passwords (which shouldn't be in code anyway), they are now public. Anything in this file is now public, so if you do indeed want to do this, be very careful about what code gets into this now client-side file.
If you need more details on how this could work, please comment :)
Edit: Also be aware that using the embedded and include methods would not allow the variable to be updated on-the-fly. If you need the client to track any changes to the data as it changes on the server, you will want to use AJAX. This is the best method, in my opinion. It offers you the most flexibility.
Another edit: Judging by the issues on the express-expose project, it seems not well supported and maybe has security issues. For now, I would avoid it. Maybe at a later time, it will be helpful and secure.
If you want to expose a variable value to the client from server , that never changes or updates, you can simply add a hidden input field in your template and add the value of it
In your EJS template file this would look like
<% if (data) { %>
<input type="hidden" value="<%= data %>" id="myGlobal"/>
<% } %>
you must pass the data value in your res.render('template',{data:"hello"}) function to the template.
You can access this variable value from client using a simple document.getElementById("myGlobal").Again don't pass any sensitive information in hidden fields.
If you want to pass sensitive information , implement an endpoint in your server with authentication and call the endpoint from client via an ajax call .This is also a best approach to update the value of the variable from client side.
I am using require('babel-register') to allow for ES6 in node.js and doing server-side rendering of my react components.
In this talk (start around 7:14)
https://youtu.be/PnpfGy7q96U?t=7m14s
one main recommendation to speed up React Server Side Rendering performance by 30+% is to point ReactDOMServer to the client-side version of the build
e.g. instead of
var ReactDOMServer = require('react-dom/server')
use
var ReactDOMServer = require('react/dist/react.min')
The reason for the speed improvement is because client-side version avoids checks to process.env.NODE_ENV which turns out to be an expensive operation.
This trick seemed to work great before but in React 15 the client side code no longer directly exposes the methods for renderToString and renderToStaticMarkup. Instead it has been hidden behind the scary looking property __SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:
You can thus do something like
var ReactDOMServer = require('react/dist/react.min').__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
But obviously this doesn't seem like a good idea (probably will not be forward compatible with future versions of React).
I tried require('react/dom-server/dist/react-dom-server.min') but this returns an 'undefined' (I believe that wrapper code is meant to run on the client not the server).
Are there any other suggestions for achieving this performance gain that are more sanctioned?
For reference the original issue is documented here but the issues seems to have been closed:
https://github.com/facebook/react/issues/812
I'm trying to build my app with React and Node (Isomorphic Rendering Architecture). I found on github example project but i have problem. I would like to develop my project client and server together, that the same Component can gets data/actions whataever from client nad server simultaneously. For example:
var Component = React.createClass({
render: function() {
return (
<div className="commentBox">
{this.props.client}
{this.props.server}
</div>
);
}
});
You can see that, Component gets props from client and server together. How i can do this?
I tryed 3 github projects but always i can't implement it. I dont know why. of course it's working when i render Component only by server or only by client but it's not working together.
For example when I render Component by server i can't make any actions specific for client (onclick alerting etc.) . So that's why it's important for me. Rendering some data from server and makes some client actions. But together, still on the same Component.
I'm sorry for my poor english!
Jan, it's impossible to do this using React.
They don't work "at the same time".
The server-side React code works by building the HTML page as a text-string, and serving the HTML text to the client.
After the browser loads the page, the React code in the browser will attach itself to the React code that was put on the page (because the server prints out IDs for all of the components, for the browser to attach to, after).
The goal, then, is to feed data to components, instead of expecting to have access to both the browser and the server at the same time.
That way, you can use server-side code to get data for the component, and you can use client-side code to get data for the component, and the component won't care.
This is not quite valid React, or the right way to do JS, in general but have a look:
class ServerElement {
render ( ) {
// sync calls should rarely ever (ideally never, other than booting up) be used
var articles = db.syncGetArticles();
return <Articles articles={ articles } />;
}
}
class BrowserElement {
render ( ) {
// isn't real, and should never be used even if it was
var articles = ajax.sync("GET", "/articles");
return <Articles articles={ articles } />;
}
}
The important part here is not the Server or Browser element (like I said, that's not really going to work), but rather that the <Articles /> element isn't expecting a server or a browser; it's expecting a list of articles.
The benefit of this approach, then, is that the server builds the HTML, but before the page is served, it's pre-filled with data, which will later be updated (replaced or added to) on the browser.
I hope that helps; if it doesn't, ask away, and I'll try to add to the answer.
#Norguard Thank you for your comprehensive answer. I am trying to own your answer. I know that your example code is not valid for React/JS cuz we have to build our db actions in models area. But one thing puzzles me. We are sending API with our '/articles' and gets data from this. OK, its cool, but this is still public data. I wonder about the private data. How to use React Isomorphic to get specific data or server if/else condition to build better app.
If we are using client-side templating language (like ejs) it's very easy. We are building our .html file and injection server methods(or whatever) to specific tags for templating language. How do to the same in React server? I can't imagines this using components and server.
I think that I understand idea you showed me but need time to efficiently build Isomorphic app using React.
I want to share a piece of coffeescript code between the server and the client on Express.
I linked the file from the server directory into the /public directory, so it can be loaded by the client.
It does have external dependencies, which I resolve statically for the client. To remove the error messages when the client tries to call require, I thought a simple conditional declaration would do.
console.log require
unless require?
require = (what) ->
console.info "this is the client, you asked me to load #{what}"
return {}
However, when run on the server, undefined will be printed and require will be overridden. The same happens for embedded Javascript:
`if( typeof require == "undefined" )
var require = function(what) {
console.info( "this is the client, you asked me to load "+what );
return {};
}
`
If I only run:
console.log require
on the server, it prints an object structure, as expected.
It seems that require is injected after at least the conditional has been evaluated and executed.
How can I override require safely, or what other paradigm might I use for sharing code between client and server?
I recommend using browserify for sharing code between the client and server. It allows you to write your javascript as you would for the server following the common js pattern, but provides a mechanism to build your module for client-side where require() works in the browser.
CoffeeScript is supported with browserify via a transform process. Coffeeify is one implementation of a transform implementation for CoffeeScript.
I am using something like airbnb's rendr, i.e. my own implementation of sharing backbone code between client and server, to build full HTML on the server using the same Backbone models, views, collections and templates I am using on the client. My files/modules are defined as requirejs modules, that way I can share them easily on the client.
In development mode, I want requirejs to refetch/reload any modules from disc when I refresh my browser (without restarting the server), so I get my server rendering uses the newest templates and javascript to finally serve me the newest HTML.
when using requirejs on the server with nodejs, the trick of appending bust parameters to the urlArgs like the following doesn't work, i.e. the server doesn't reload/refetch any modules from disc
urlArgs: "bust=v2"
I wonder, if reloading/refetching requirejs modules from disc-space without restarting the server is possible in node? specifically, that would be very useful for the require-text plugin for template. additionally, it would be nice to apply reloading only to a restricted set of modules.
I've never had to do this, but a quick search shows a few options, including automating the server restart:
nodemon
node-supervisor
You may also be able to do something similarly creative to this:
delete require.cache['/home/shimin/test2.js']
You could almost definitely clear the version RequireJS has in cache, forcing it to reload it, although I suspect the node would just serve up the old file again.
Finally, maybe take a look at hot-reloading, which seems to work around the caching without needing to restart the server (example).
Like the OP, I was looking for a way to make development in node with RequireJS easier and faster. In my case, I am using RequireJS with a grunt watch task in node, and wanted to be able to force RequireJS to always get the latest version of a module from the file system. And happily, I found a very simple solution!
I had to slightly modify Henning Kvinnesland’s suggestion by adding the timeout, but now it is working like a charm:
var requirejs = require('requirejs');
// Set up requirejs.config here...
// Disable caching of modules
requirejs.onResourceLoad = function(context, map) {
setTimeout(function() {
requirejs.undef(map.name);
}, 1);
};