18

I'm trying to use isomorphic rendering in React so I can output static HTMLs as documentation for my application.

The problem is that I have a particular component that only runs on the client, because it references window. The solution is obvious: Not to render it on the server. Yes, I can not to render it on the server, but still, I need it to be included in my webpack bundle so I can render it on the client. The problem is that, the code that prevents my component from rendering on the server is:

function isServer() {
    return ! (typeof window != 'undefined' && window.document);
}

But isServer() is also true when webpack is bundling, and I want it to work normally while webpack is running.

So, how do I detect that webpack is running?

I'm looking for something like this:

function isWebpack() {
    // what do I put here?
}

Now I can render my client-only component normally if isServer() and !isWebpack().

Thanks!

EDIT

This is the component I'm trying to build:

function isServer() {
    return ! (typeof window != 'undefined' && window.document);
}

import React from 'react';

const LED = React.createClass({

    render: function () {

        if(!isServer()) {
            var LiveSchemaEditor  = require('../../src/components/LiveSchemaEditor.js');
            return <LiveSchemaEditor />;
        }

        return <div>I AM IN THE SERVER</div>;
    }
});

export default LED;

What's bugging me is that the webpack bundle includes the content of LiveSchemaEditor.js but it still prints I AM IN THE SERVER while on client. This doesn't make sense.

Andre Pena
  • 56,650
  • 48
  • 196
  • 243
  • Would checking for something node-y work? `function isNode() { return process && typeof process.env === 'object' && Object.keys(process.env).length; }` On the browser, that returns `0`, but running in node, it will be > 0. Or just look for any of this: https://nodejs.org/docs/latest/api/process.html#process.version – m59 Aug 26 '15 at 02:57
  • @m59, thank you, but that will return true while server rendering too. I need something that returns true only if webpack is running. If I could set `process.env.NODE_ENV` specifically for when webpack is running, that would do the trick. – Andre Pena Aug 26 '15 at 03:00
  • I don't use webpack, so could you show me how to setup the circumstance you're referring to? Webpack is actually executing your code? – m59 Aug 26 '15 at 03:03
  • that's going to be a little hard for you since you're not used to it :). Webpack is a bundling tool. It traverses CommonJs `require` statements to build a tree of JavaScript files and outputs a resulting bundle. I appreciate that you are trying to help. – Andre Pena Aug 26 '15 at 03:05
  • I use browserify all the time and I've used webpack just a touch before. Shouldn't be hard. What I don't get is how webpack itself will be running your function in the bundling process. Webpack doesn't execute the code it's bundling. You either have code executing at runtime in the browser or in the server, so only a check whether it's running in a browser or server matters, right? – m59 Aug 26 '15 at 03:09
  • @m59 You are right, webpack doesn't execute the code it's bundling. You made me think about it. I just edited my question with an update about it. Unfortunately I have to sleep now. Thanks for your help. I'll check this thread again tomorrow. Good night. – Andre Pena Aug 26 '15 at 03:25
  • The first thing to simplify your thinking is that `LiveSchemaEditor` is going to be in the bundle no matter where you `require()`. It might be easier on your mind if you move that `require()` to the top of the file. The `if` statement is irrelevant to the bundling process. As far is your `isServer` function, I just made a file and bundled it with webpack, then tested the bundle in the browser and with node, and both returned appropriately (browser: false, node: true), so it seems impossible for the code you have here to produce the results you're getting. Could it be something else? – m59 Aug 26 '15 at 03:56

6 Answers6

20

Put this in your webpack config under plugins:

new webpack.DefinePlugin({
    'process.env': {
        NODE_ENV: JSON.stringify('production'),
        APP_ENV: JSON.stringify('browser')
    }
}),

With that you can check if you're running in a browser or not this way:

if (process.env.APP_ENV === 'browser') {
    const doSomething = require('./browser-only-js');
    doSomething();
} else {
    const somethingServer = require('./server-only-js');
    somethingServer();
}

if (process.env.APP_ENV !== 'browser') {
    const somethingServer = require('./server-only-js');
    somethingServer();
}

Because these environment variables get replaced during the build, Webpack will not include resources that are server-only. You should always do these kinds of things in an easy way, with a simple, direct compare. Uglify will remove all dead code.

Since you used a function before and the function is not evaluated during build, Webpack wasn't able to know what requires it could skip.

(The NODE_ENV-variable should always be set to production in production mode, since many libraries including React use it for optimisations.)

Benny Code
  • 51,456
  • 28
  • 233
  • 198
Ambroos
  • 3,456
  • 20
  • 27
  • That tag-on at the end about the NODE_ENV environment variable looks so valuable I fear I have missed it somewhere obvious. Where should I have seen it? Maybe I haven't seen it because I don't happen to be using any of those libraries? – BaldEagle Sep 11 '16 at 23:23
  • @BaldEagle, it's listed on React's downloads page in the NPM section: https://facebook.github.io/react/downloads.html#npm - I just think few people read it anymore. – Ambroos Sep 12 '16 at 09:35
  • That's part of the answer: I'm not yet using React. That time is coming. In case it helps, I did some web searching and found this interesting: [link](http://www.hacksparrow.com/running-express-js-in-production-mode.html) Thanks for writing. – BaldEagle Sep 13 '16 at 18:56
  • 1
    You will end up rewriting the entire `NODE_ENV` if you pass it to `DefinePlugin` as an object. Instead: `"process.env.NODE_ENV": JSON.stringify(...), "process.env.APP_ENV": ...` – Walter Feb 01 '17 at 22:37
12

You could also do this -

typeof __webpack_require__ === 'function'

I'm guessing this might change at anytime so use with caution. :/

Josh Unger
  • 6,717
  • 6
  • 33
  • 55
1

in Node.js global.global is cycle reference, Webpack is not creating this cycle:

function is_node() {
    return typeof global !== 'undefined' && global.global === global;
}
jcubic
  • 61,973
  • 54
  • 229
  • 402
0

Here's an improved version of @Ambroos ' answer.

// webpack.config.js
const DefinePlugin = require('webpack').DefinePlugin;

const config = {
    ...
    plugins: [
        new DefinePlugin({
            'DEBUG': false,
        }),
    ],
};

module.exports = (env, argv) => {
    switch (argv.mode) {
        case 'development': {
            config.plugins[0].definitions.DEBUG = true;
            break;
        }

        case 'production': {
            break;
        }

        default: {
            throw new Error('No matching configuration was found!');
        }
    }

    return config;
};
8ctopus
  • 2,617
  • 2
  • 18
  • 25
-1

Changing the output of render() cause react to fail rehydrating the component, so serverside rendering will be discared.

Instead, consider using ComponentDidMount, which runs exclusively in the browser:

//file level const (cache the result)
let LiveSchemaEditor = () => null;

//...

componentDidMount() {
    LiveSchemaEditor  = LiveSchemaEditor || require('../../src/components/LiveSchemaEditor.js');
    this.setState({ editor: <LiveSchemaEditor/> });
}

render() {
    if(!this.state.editor){
        return <div>loading...</div>;
    }

    return this.state.editor;
}
Uri Kutner
  • 476
  • 4
  • 13
-1

This is what worked for me.

if(typeof process.env.NODE_ENV === "undefined") {
    // Not webpack
} else {
    // Its webpack
}
Ghustavh Ehm
  • 49
  • 2
  • 5