0

I'm trying to accomplish server side rendering with React components in Express and templating with Jade. However I'm running into some issues.

Here is my server code, the entry point.

var app = require('express')();
var http = require('http').Server(app);
var React = require('react');
var ReactDOMServer = require('react-dom/server');

//Component
var Index = require('./views/components/index.jsx').default;

app.set('port', process.env.PORT || 3000);
app.set('views', `${__dirname}/views`);
app.set('view engine', 'jade');

app.get('/', (req, res) => {
  var reactHTML = ReactDOMServer.renderToString(<Index/>);
  res.render('test', { reactHTML: reactHTML });
});

http.listen(3000, () => {
  console.log(`listening on port ${app.get('port')}`);
});

Here is my Index component, it's actually just rendering a div with my ChatTextBox component, which is this:

import React, { Component } from 'React';

class ChatTextBox extends Component {

  constructor(props) {
    super(props);
    this.state = { term: 'test' };
    this.termChange = this.termChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    console.log('this was clicked');
    this.setState({ term: '' });
  }

  termChange(event) {
    console.log(event.target.value);
    this.setState({ term: event.target.value });
  }

  render() {
    return (
      <div>
        <input type="text"
        value={this.state.term}
        onChange={this.termChange} />
        <button onClick={this.handleClick}>Send Message</button>
        <div>{this.state.term}</div>
      </div>
    );
  }
}

export default ChatTextBox;

edit:

Here is my jade template, test.jade

html
  body
    div#container
      #{reactHTML}

Problems: 1) It's not rendering properly. Here is the result: Imgur

It's rendering the component twice, along with the '<', '>' symbols..?

2) None of the hooks work, in terms binding state and rendering dynamically. This makes sense though, I'm rendering static HTML in jade. So how would I accomplish this dynamically? (I think this is a bit off topic, I can make a separate concern for this).

Thank you for reading, hopefully this post was clear enough.

  • how are you getting it into Jade or whatever template you're using? The usual way is to send the actual `renderToString()` result to the client – markthethomas Mar 30 '16 at 00:32
  • see, for example https://ifelse.io/2015/08/27/server-side-rendering-with-react-and-react-router/ – markthethomas Mar 30 '16 at 00:32
  • that is what I'm doing, I'm passing the output in res.render('test', ...) in my app.get('/') route handler. I've attached my jade template. –  Mar 30 '16 at 00:35
  • there's not really a need for Jade if you're doing pre-rendering the html to send down. I'm guessing it's not properly escaping the html; you can try using the `!=` operator from Jade though and see if that helps – markthethomas Mar 30 '16 at 00:37
  • If you *are* doing rendering server-side with React, you probably don't need Jade. You'll essentially be doubling-up on renders and bc Jade caches in production I'm guessing you'd run into some errors when React needs to send a different rendered string down. – markthethomas Mar 30 '16 at 00:39
  • sorry, so what is the solution here then? to use something like react-router to handle routing? –  Mar 30 '16 at 00:52
  • 1
    sorry — try using `!=ReactContent` or whatever else Jade will allow you to do to not escape the string. The other solution would be to directly send down the rendered string to the client instead of relying on Jade. Does that help? Pretty much, you don't really need Jade – markthethomas Mar 30 '16 at 00:53
  • it worked! thanks so much.. now to figure out how make this dynamic.. Could you write an answer so I could mark as accepted? –  Mar 30 '16 at 01:03
  • Sure thing! I'll answer l – markthethomas Mar 30 '16 at 01:09

1 Answers1

0

It looks like you're using Jade potentially redundantly. The usual approach with reactDOM server-side is to send the result of renderToString() down to the client. If you do need to keep using Jade for whatever reason, you can tell it to unescape HTML strings with !=. So, you can change your template to:

html
  body
  div#container
   #{!=reactHTML}

That should ensure it's not double-rendering everything

markthethomas
  • 4,391
  • 2
  • 25
  • 38