Ahsan Ahmed
Ahsan Ahmed

Reputation: 133

React.JS - Completely not Mounting in Node - Express View Template

I am New in Learning to create The web server application using Node-Express-React but I couldn't React Fully. In This Code I tried to use componentDidMount() function in React but somehow this piece of code is not firing it .. Searched all the Way to Internet but couldn't find a way to use it with only Express Node and React. Have learned about the Webpack which turns out to create complexity in my code

Here is the Server Side server.js code to set my views and stuffs

const express = require('express');    
const app = express();    
const path = require('path');  
const React = require('react'); 
//setting my CSS folder (works fine)
app.use(express.static('static_assets'));

app.set('views','./views' );
app.set('view engine','jsx');
app.engine('jsx',require('express-react-views').createEngine());

app.get('/',function (req,resp){
    resp.render('app',{});
});

app.listen(5000,function(){
    console.log("Server Running at 5000");
});

and here is my React app.jsx Code

var React =require( 'react');

class Text extends React.Component{
    constructor(props){
        super(props);
        this.state = {speed:10};
    }
    componentDidMount(){
        this.setState({speed:30});
    }
    render(){
        return(
              <div>
             <h1>Hello </h1>
           {this.state.speed}
        </div>
        );
    }
} 
class app extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return(
              <Text />
        );
    }
}

module.exports= app;

I have used this same code of React side and created a web page of HTML from scratch and copied it there and imported React.js and ReactDOM.js CDN's and this same code works fine on Static HTML pages but not on Express Node Server for Some Reason

if there exist any way that I can make componentDidMount() trigger or Make a State change after it is being Rendered??

Upvotes: 0

Views: 2106

Answers (3)

Ahsan Ahmed
Ahsan Ahmed

Reputation: 133

My Question was that React's Lifecycle Method was not Completely invoked when i tried to Use Express React View Template engine in the Above Code. The Problem Was that I tried to Load All the React code using this simple template. It Just Loads all The HTML only. I didn't looked into the official React Docs of Installation My Answer was there . Thanks to Mark Rabey's Answer that made me Realize what I was Doing Wrong. According to the Official Docs on Link https://facebook.github.io/react/docs/installation.html that Solved my Problem. The Main Text for Installing Right React to your Application is :

You don't need to rewrite your app to start using React.

We recommend adding React to a small part of your application, such as an individual widget, so you can see if it works well for your use case.

While React can be used without a build pipeline, we recommend setting it up so you can be more productive. A modern build pipeline typically consists of:

  • A package manager, such as Yarn or npm. It lets you take advantage of a vast ecosystem of third-party packages, and easily install or
    update them

  • A bundler, such as webpack or Browserify. It lets you write modular code and bundle it together into small packages to optimize load time.

  • A compiler such as Babel. It lets you write modern JavaScript code that still works in older browsers.

This Made me Realize that the missing thing in my code is the WebPack or Broserify and I have to remove express-react-view to make My React Fully Working. Thanks to The Help of RemarkableMark on Guiding me Step-by-Step on setting My React on this PlayList (https://www.youtube.com/watch?v=k66bOHX8MnY&list=PLVgOtoUBG2md5HxaABCcnfstF88CPzUeD) I have Successfully Created my Own Template on React Connected with Express-Node Successfully. You can Get it From This Github Link github.com/Ahsan-J/FirstExpressReactNodeLoginApp/tree/master/Express-React-Webpack . Thank You All of The Stack Community on Helping Out on my First Question :')

Upvotes: 0

Mustansir Zia
Mustansir Zia

Reputation: 1004

As Mark said, you aren't actually rendering a React component inside your web page but a simple HTML markup generated by the jsx templating engine. First, we change your templating engine to a more conventional one like hjs. Then, to make your web app behave exactly like a React app we need to install React on the page and then mount your component. Apart from this, we'll also use node to render our component on the server and send that to the client. So we'll do server side rendering plus we'll also mount components inside your web app so your life cycle methods can run.

• The key point to note here is that the component will be rendered on the server but won't be mounted before it's actually mounted inside the browser.

• Another key point to note will be that no DOM manipulations will occur since the same markup has already been rendered on the server and handed over to the client inside react-root using {{react}}. So the diff will result in zero changes and the overall rendering will be fast. This is a fairly simple solution and you won't have to modify much code.

Your app.js should like this.

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var React = require('react');
var ReactDOM = require('react-dom/server');
// Will allow us to require .jsx files and to compile them.
var jsx = require('node-jsx');
jsx.install();

var HelloMessageComponent = require('./public/jsx/App.jsx');
var HelloMessage = React.createElement(HelloMessageComponent);


var app = express();

// view engine setup. Change this to hjs.
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hjs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', function (req, res) {
    res.render('index', {
        react: ReactDOM.renderToString(HelloMessage, {name: 'John'})
    });
});


Your views/index.hjs should like this.

    <!DOCTYPE html>
<html>
  <head>
    <title>{{ title }}</title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <!-- Notice we're installing React here too via CDN. This will mount our already pre-rendered components -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
    <script crossorigin src="https://unpkg.com/react@15/dist/react.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
  </head>
  <body>

  <!-- This is where our server side rendered component code will go. -->

    <div id="react-root">{{react}}</div>

  <!-- Mount the same component inside the App.jsx file from the browser. -->

    <script src="/jsx/App.jsx" type="text/babel"></script>

  <!-- The server will create a full static markup inside the react-root div. -->
  <!-- Once we go to localhost:3000 from our browser the above script will run and mount the same component inside -->
  <!-- the same div. The key here is that no DOM manipulations will occur since the same markup has already been rendered -->
  <!-- inside react-root using {{react}}. So diff will result in zero changes and the rendering will be fast. -->
  <!-- Your React app is now mounted as well as rendered from the server. -->
  <!-- You can of course further optimize your text/babel script tag by using browserify to precompile and bundle. -->
  <!-- The link for bundling is down below! -->

  </body>
</html>


Lastly, your component App.jsx (public/jsx/App.jsx) should look like this. Please note the isNode flag to modify our global variables since the same code will run inside the browser and the server.

var isNode = typeof module !== 'undefined' && module.exports
    , React = isNode ? require('react') : window.React
    , ReactDOM = isNode ? require('react') : window.ReactDOM;

class HelloMessage extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            name: this.props.name
        }
    }

    componentDidMount() {
        console.log('mounted');
        setTimeout(() => {
            this.setState({name: 'Mustansir!'})
        }, 5000);

    }

    handleClick() {
        alert('clicked!');
    }

    render() {
        return <div onClick={this.handleClick}>Hello {this.state.name}</div>;
    }
}


if (isNode) {
    module.exports = HelloMessage
} else {
    ReactDOM.render(<HelloMessage name="John"/>, document.getElementById('react-root'))
}


Learn more about further optimizing your client side code using browserify from here.

You can also plug in your react-router and react-redux for an even better server side rendering of your React components. (If you plan to use those of course)

That however would now make this answer way too long and it's already quite long.

Really hope it helps!

Upvotes: 1

Mark Rabey
Mark Rabey

Reputation: 1415

I believe your issue is that because you're not using Webpack or something else to transpile or bundle your code, you're not really using React. It's never loaded on the page. There is no ReactDOM.render() or anything needed to actually make use of React. You're using express-react-views which only works as a jsx view engine for Express.

From their README:

This is an Express view engine which renders React components on server. It renders static markup and does not support mounting those views on the client.

React isn't actually on the page, therefore your components lifecycle methods are never being called. Basically, it's taking what the component would look like first render, and serving that to the browser as HTML.

Upvotes: 2

Related Questions