1

I'm trying to create a server rendered react app, the only part I'm stuck on is importing my components to my express server and getting the static markdown to send back to the user. Essentially what I have right now is this:

Express server:

const Report = require('../public/source/components/index.js').default;
....
router.get('/*', function(req, res, next) {
    var reportHTML = ReactDOMServer.renderToStaticMarkup(react.createElement(Report)))
    res.render('index', { title: 'Report' });
});

When I hit that route, I get the following error:

Warning: React.createElement: type is invalid -- expected a string
(for built-in components) or a class/function (for composite components)
but got: object. You likely forgot to export your component from the file
it's defined in. Check the render method of `ReportApp`.
in ReportApp

The contents of my index.js file, note that I stripped out a lot of the complexity involving graphql and setting the initial state, which is why this isn't a functional component.

import React, { Component } from 'react';
import Header from './header/Header';
import PageOneLayout from './pageOneLayout/PageOneLayout';
import styles from './main.scss';

const hexBackground = require('./assets/hex_background.png');

export default class ReportApp extends Component {
  render() {
    return (
      <div className={styles.contentArea}>
        <img src={`/build/${hexBackground}`} alt={'hexagonal background'} className={styles.hexBackground}/>
        <Header client={"client name"} />
        <div className={styles.horizontalLine}></div>
        <PageOneLayout chartData={this.state} />
      </div>
    )
  }
}

Any pointers in the right direction would be appreciated!

EDIT:

here's my webpack:

/* eslint-disable no-console */
/* eslint-disable import/no-extraneous-dependencies */
import autoprefixer from 'autoprefixer';
import nodemon from 'nodemon';
import ExtractTextPlugin from 'extract-text-webpack-plugin';

nodemon({
    script: './bin/www',
    ext: 'js json',
    ignore: ['public/'],
});

nodemon.on('start', () => {
    console.log('App has started');
}).on('quit', () => {
    console.log('App has quit');
}).on('restart', files => console.log('App restarted due to: ', files));

export default {
    watch: true,
    entry: './public/source/main.js',
    output: { path: `${__dirname}/public/build/`, filename: 'main.js' },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: 'babel',
                query: {
                     presets: ['react', 'es2015', 'stage-1'],
                     plugins: ['transform-decorators-legacy'],
                     cacheDirectory: true
                 }
            },
            // {
            //     test: /\.jsx?$/,
            //     exclude: /node_modules/,
            //     loader: 'eslint',
            // },
            {
                test: /\.s?css$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader!sass-loader?outputStyle=expanded&sourceMap')
            },
            { test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$|\.wav$|\.mp3$/, loader: "file", output: {path: `${__dirname}/public/build/`, filename: 'logo.svg'}},
        ],
    },
    // eslint: {
    //     configFile: './public/.eslintrc',
    // },
    resolve: {
        modulesDirectories: ['node_modules', 'public/source'],
        extensions: ['', '.js', '.jsx'],
    },
    postcss: [
        autoprefixer,
    ],
    plugins: [
        new ExtractTextPlugin('main.css', { allChunks: true }),
    ],
};
Colton
  • 218
  • 1
  • 2
  • 10

1 Answers1

1

There are few things to consider:

  • Are you doing any code transpiling at server side?
  • How are you building your component bundle(show the config, I assume webpack)?
  • Make sure the bundle component exposes the component.
  • The extra createElement shouldn't be needed in this case ReactDOMServer.renderToStaticMarkup(react.createElement(Report))).
Risto Novik
  • 8,199
  • 9
  • 50
  • 66
  • I'm using `babel-register` on my server, otherwise I'm just importing my main component and calling those methods on it. I also updated my original post with my webpack config – Colton Jun 13 '17 at 18:11
  • Try removing the `createElement` ReactDOMServer.renderToStaticMarkup(Report)) – Risto Novik Jun 13 '17 at 18:13
  • That change yielded this error: `renderToStaticMarkup(): You must pass a valid ReactElement.` – Colton Jun 13 '17 at 18:15
  • I've seen other people using jsx on in their server code, not sure exactly how they accomplished that but maybe that could lead us in the right direction? – Colton Jun 13 '17 at 18:24
  • As you use babel-register the code should be transpiled on the fly what if you try server side ReactDOMServer.renderToStaticMarkup(
    Test
    );?
    – Risto Novik Jun 13 '17 at 18:28
  • I get `SyntaxError: Unexpected token <` – Colton Jun 13 '17 at 18:29
  • Take a look at this answer for setting up the babel register correctly, it seems like there is something wrong, make sure react preset is included https://stackoverflow.com/a/35002082/261783. – Risto Novik Jun 13 '17 at 18:44
  • I updated my babel-register to include both react and es2015, however I'm getting the same errors, either unexpected tokens when trying to use jsx, or `renderToStaticMarkup(): You must pass a valid ReactElement.` when I just pass in the `Report` variable – Colton Jun 13 '17 at 18:50
  • Rendering simple div to string works `return ReactDOMServer.renderToStaticMarkup(React.createElement("div"));`? – Risto Novik Jun 13 '17 at 19:18
  • It works as expected, it also works if I pass my report component to `renderToStaticMarkup` on the front end and log it there – Colton Jun 13 '17 at 19:27
  • Add `debugger;` statement in your route before hitting the `renderToStaticMarkup` run `node --inspect index.js` next open dev tools in web and hit the route. Now you should see the exact scope of your component, try running Report({}) does it return correct react component, try inspecting that component? – Risto Novik Jun 13 '17 at 19:35