How to export Javascript class which can be shared by NodeJS and ES6? - node.js

I have an Electron app integrated with Flask server. The app has both HTML loaded by Flask server (flask-page.html ES6) and Electron renderer (render-page.html NodeJS).
I want to share a class defined in a file for Flask and Renderer to avoid duplicated code, as class is exactly the same, but with different export/import.
Now, it's defined in 2 files as followings:
sharedclass-nodejs.js
class sharedClass {
sameMethod() {
}
}
module.exports = {sharedClass}
sharedclass-es6.js
export class sharedClass {
sameMethod() {
}
}
load-flask.js
import {sharedClass} from "sharedclass-es6.js"
flask-page.html (Flask server)
<script type="module" src="sharedclass-es6.js"></script>
<script type="module" src="load-flask.js"></script>
load-render.js
const {sharedClass} = require("sharedclass-nodejs.js")
render-page.html (Electron render UI)
<script src="sharedclass-nodejs.js"></script>
Is it possible to define 1 copy of "class sharedClass" in 1 file, such as NodeJS style export, and use it in ES6?

Related

How to achieve code splitting + SSR with #loadable/component ? Is webpack needed for the server code?

I'm trying to add code splitting + SSR to my React web app using #loadable/component.
My current situation: This is how I've implemented SSR for robots on my website. Since it's just for robots, I'm not using hydrate. Basically, I send either the index.html with the JS bundle's script tags for a regular user, or I send a fully rendered HTML page for the robots, without the need to hydrate.
My goal: I'd like to use #loadable/component to always return SSR pages from my server, and use hydrate to attach my JS bundle. And also achieve code splitting with that.
Here is how I currently build my app (pseudo code):
1. webpack BUILD FOR entry { app: "src/index.tsx" } AND OUTPUT BUNDLES TO MY /public FOLDER
2. babel TRANSPILE WHOLE `/src` FOLDER AND OUTPUT FILES TO MY /dist_app FOLDER
It's basically 2 builds, one of them is using webpack to bundle, and the other one basically transpiles the files from src to distApp.
And this is what my server does (pseudo code)
1. CHECK IF USER IS ROBOT (FROM THE USER AGENT STRING)
2. IF REGULAR USER
res.send("public/index.html"); // SEND REGULAR index.html WITH JS BUNDLES GENERATED BY WEBPACK
IF ROBOT
const App = require("./dist_app/App"); // THIS IS THE src/App COMPONENT TRANSPILED BY BABEL
const ssrHtml = ReactDOM.renderToString(<App/>);
// ADD <head> <helmet> <styles> ETC
res.send(ssrHtml);
The steps described above works just fine for my initial requirements (ssr just for robots).
But after I added #loadable/component to achieve code splitting + SSR, the set up above does not work anymore.
Because now I have dynamic imports on some of my routes. For example:
const AsyncPage = loadable(() => import("#app/pages/PageContainer"));
So my renderToString(<App/>) call comes back mostly empty, because it does not load those AsyncPages.
Over on the docs for Loadable components: server side rendering they have an example repo on how to achieve this.
But their example is kind of complex and it seems they are using webpack inside the server. I'll post what they do on their server below.
QUESTION
Do I really have to use webpack to bundle my app's server code in order to use #loadable/component for SSR like they are showing in their example? Can't I just use some kind of babel plugin to convert the dynamic imports into regular require calls? So that I'll be able to render it the way I was doing before?
It's weird, that they use webpack-dev-middleware. It's like this example should be used just for development. Does anybody know a repo with a production example of this?
import path from 'path'
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '#loadable/server'
const app = express()
app.use(express.static(path.join(__dirname, '../../public')))
if (process.env.NODE_ENV !== 'production') {
/* eslint-disable global-require, import/no-extraneous-dependencies */
const { default: webpackConfig } = require('../../webpack.config.babel')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
/* eslint-enable global-require, import/no-extraneous-dependencies */
const compiler = webpack(webpackConfig)
app.use(
webpackDevMiddleware(compiler, {
logLevel: 'silent',
publicPath: '/dist/web',
writeToDisk(filePath) {
return /dist\/node\//.test(filePath) || /loadable-stats/.test(filePath)
},
}),
)
}
const nodeStats = path.resolve(
__dirname,
'../../public/dist/node/loadable-stats.json',
)
const webStats = path.resolve(
__dirname,
'../../public/dist/web/loadable-stats.json',
)
app.get('*', (req, res) => {
const nodeExtractor = new ChunkExtractor({ statsFile: nodeStats })
const { default: App } = nodeExtractor.requireEntrypoint()
const webExtractor = new ChunkExtractor({ statsFile: webStats })
const jsx = webExtractor.collectChunks(<App />)
const html = renderToString(jsx)
res.set('content-type', 'text/html')
res.send(`
<!DOCTYPE html>
<html>
<head>
${webExtractor.getLinkTags()}
${webExtractor.getStyleTags()}
</head>
<body>
<div id="main">${html}</div>
${webExtractor.getScriptTags()}
</body>
</html>
`)
})
// eslint-disable-next-line no-console
app.listen(9000, () => console.log('Server started http://localhost:9000'))

How to use the file on the backend and frontend at the same time?

I am using nodejs server.
I have a file with a class in which the functionality is written.
The functionality of this class is the same for the backend and for the frontend.
I do not want to create two files with the same functionality.
A file with a class has dependencies - other classes in other files.
How to connect files on the backend and frontend?
What should the classes look like for this?
nodejs back-end app.js:
let player = new Player();
player.goTo(10,20);
front-end index.js:
let player = new Player();
player.goTo(10,20);
It's files need include to frontend and backend:
Player.js:
Class Player
{
goTo(x,y){
Path.pathFinding()
}
}
Path.js:
Class Path
{
pathFinding(){
//doing something
}
}
You are going to want to put this .js file in the folder from where you are hosting static files to serve to the frontend. Whether this be /public or /views or whatever name you called this folder. And in the file next to your class you type in the keyword export: export class {...}. I'm sure you have already done this but no assumptions. Then in your server file simply import it into your server file like this:
import myClass from "/public/js/myjsfile.js";
Example:
public/js/dog.js:
export class dog {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
server.js:
import dog from "/public/js/dog.js";
The dependencies that are used in dog.js or the client side file will come along with it when you import the file on the backend.

Using Tau-Prolog with React

I am trying to use Tau-Prolog with Node.js and React.
At the step:
session.consult(program);
I get this error message:
TypeError: fs.existsSync is not a function
Here is the code to reproduce on the problem:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
// These alternatives makes no difference:
var pl = require('tau-prolog');
// var pl = require('./tau-prolog/modules/core.js');
// var pl = require('./tau-prolog.js');
class App extends React.Component {
componentDidMount() {
let program = 'fruit(apple). fruit(banana).';
let session = pl.create();
// Until here, it's ok. I get Session {rules: {…}, src_predicates: {…},
// The trouble is at this step:
session.consult(program);
////////
//////// TypeError: fs.existsSync is not a function
////////
}
render() {
return <div>Hello world</div>;
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Thanks for your help!
You don't require it on server side, except if you want to evaluate the prolog program on server side and request the result.
If you just want to use it with react in the frontend, you can simply load it like any other frontend library by adding it to your html
<script type="text/javascript" src="tau-prolog.js"></script>
<script type="text/javascript" src="tau-prolog/core.js"></script>
<script type="text/javascript" src="tau-prolog/lists.js"></script>
and then either fetch an external .pl file which contains the Prolog code, or use it inline like in the examples or in react.

How to Call oidc-client signinSilentCallback in a ReactJs Component

I need to implement the Slient-Renew Token using oidc-client or redux-oidc - npm node module.
I'm using Identity Server Version 3.0 and Javascript ReactJs Client UI Application (Webpack Version 2).
I'm having a simple Javascript application download from github, the Slient Renew HTML file is
<!DOCTYPE html>
<html>
<head>
<title>Silent Renew</title>
<meta charset="utf-8" />
</head>
<body>
<script src="./oidc-client.js"></script>
<script>
new Oidc.UserManager().signinSilentCallback();
</script>
</body>
</html>
Currently I created a Route
<Route exact path='/SilentRenew' component={SilentRenew} />
The Component code is
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import UserManager from 'oidc-client';
/**
* <p>.</p>
* #extends Component
*/
class SilentRenew extends React.Component {
constructor(props) {
super(props);
alert('Hai');
const userManager = UserManager();
userManager.signinSilentCallback();
}
render() {
return (
<div>Silent Renew</div>
);
}
}
export default SilentRenew;
I'm getting error
Kindly assist me how to call the signinSilentCallback method.
I'd put the code
const userManager = UserManager();
userManager.signinSilentCallback();
inside componentDidMount() instead of constructor().
Check React Component lifecycle methods...

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.

Resources