22

React development build behaves differently than production build, e.g. error handling.

It can be figured out which one is used from the environment but only in modular environment, due to how process.env.NODE_ENV is used by React package:

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

The case when process.env may be inapplicable is React used globally as UMD module, window.React and window.ReactDOM:

<script src="some-unknown-react-version.js"></script>

<script>
React // is it in production mode?
</script>

Possible uses are:

  • a component that can work in modular and global environments (published as UMD) and renders differently in production

  • browser extension or user script where build/production mode is detected from React or ReactDOM object

How can React development/production build be accurately detected at runtime without resorting to the environment?

I'm looking for reliable and clean solution that would be applicable to React 15 and React 16 if possible.

This is not a duplicate of similar questions because existing answers address the problem through process.env.

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • How does your deployment setup look like? There is no "correct" way to do what you want to do. I would focus on differences between production/development build and create my own helper function. Look at my answer about prop-types. – Michal Oct 16 '18 at 17:57

7 Answers7

34

There is difference. In the development mode, React elements have the property _self defined, whereas in production mode that property is not defined.

So, a solution is to test for this property with a code like this:

function ReactIsInDevelomentMode(){ 
    return '_self' in React.createElement('div');
}
Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
2

Detecting dev/production build at client using a umd build seems like a long stretch. If such a requirement exists why not build your app with create-react-app ?

I am not to judge your decisions, so here is something useful.

react-dev-tools plugin provided by facebook detects the build type.

Here is the relevant part of the above mentioned plugin:

https://github.com/facebook/react-devtools/blob/faa4b630a8c055d5ab4ff51536f1e92604d5c09c/backend/installGlobalHook.js#L23

Hope you could make it useful.

Arun Karunagath
  • 1,593
  • 10
  • 24
  • Thanks! I didn't take a look at DevTools because I didn't expect they do something like that; they were just hanging in case of production build without useful message. Please, consider posting relevant code from the link to the answer, so it could be understandable without visiting offsite resources. Yes, the solution seems too hacky to use it in regular components. – Estus Flask Oct 13 '18 at 20:14
  • ReactDOM code contains a hook for React Dev Tools, so this hack is not available. The best solution is to add proper bundle types into React/ReactDOM itself. – Dolf Barr Oct 16 '18 at 18:30
  • The main target of the question was understanding build type, using a google chrome plugin how can help the developer to collect data? – AmerllicA Oct 16 '18 at 21:03
  • I'm talking about the solution in this answer: React Dev Tool contains the function which detects React build type, but it works because ReactDOM lib contains a hook for React Dev Tool and it runs on ReactDOM init. So, if ReactDOM lib can contain a hook for React Dev Tool, then it can contain build type, and the best solution is to create the pull request with proper functionality – Dolf Barr Oct 16 '18 at 21:40
  • @DolfBarr I agree that having this functionality in ReactDOM itself could be useful. Yet it's possible to use same method to hook into __REACT_DEVTOOLS_GLOBAL_HOOK__ in regular script, regardless of React Dev Tool. I wouldn't use it in regular component but may be useful for other purposes (browser extension or user script). – Estus Flask Oct 16 '18 at 23:04
2

Your question is clear but you do not clarify your build system, do you use webpack or parcel? do you have Server Side Rendering or not? do you run your built application by node or pm2? or you just build your application and then put the built bundled file inside your page that made by other technology like PHP or C#?

Actually, the above questions can determine your answer, but surely, you use module bundler, so I proffer use resolving a config file in your project.

If I was your place, undoubtedly, I use webpack, two webpack configuration files, one for development and one for production mode. then I create a folder that contains two files with config.dev.js and config.prod.js. In the development webpack:

~~~
module.exports = {
        ~~~
        resolve: {
            extensions: ['.js', '.jsx'],
            alias: {
                ~~~
                Config: `${srcDir}/config/config.dev.js`,
                // srcDir is a defined variable for source directory
            }
        },
        ~~~

In the production webpack:

~~~
module.exports = {
        ~~~
        resolve: {
            extensions: ['.js', '.jsx'],
            alias: {
                ~~~
                Config: `${srcDir}/config/config.prod.js`,
                // srcDir is a defined variable for source directory
            }
        },
        ~~~

And now you can put each dev and prod data for your build types. for example in your config.dev.js can write:

module.exports = {
    buildType: "dev"
};

Surely, in your config.prod.js can write:

module.exports = {
    buildType: "prod"
};

Absolutely you can access to the config data with below code inside your react files:

import config from 'Config';

And with this solution, you can understand the type of your build in the real-time execution of your application.

Note: For more information, you can see my medium article, And if you are not familiar with long reads see the article repository Also the newer version of the example of my answer repository that contains configs.

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
  • As I explained in comments, my own build setup doesn't matter. See 'Possible uses' in the question. E.g. component is built to UMD and can used by a third party. I'm not responsible for building the entire application, just a piece of it that needs to check whether it's development or production environment. – Estus Flask Oct 16 '18 at 22:06
  • Ok, I assumed this situation that you pointed in your comment under my answer, in any way, you should bundle your component, so you can use my way. – AmerllicA Oct 17 '18 at 03:33
  • I'm not sure how I can use that. I need to detect the environment where bundled component is used, not my own environment. I'll try to explain. The decision cannot be made at the time when I build the component. If I bundle the component as UMD with 'production' setting, and a developer then uses it in development environment, this is considered *development* environment, and this is what I'm trying to detect from `React` object. – Estus Flask Oct 17 '18 at 15:05
1

There'is a little 'hack' which can be done for checking which version of React has been loaded.

React object is available in global variables, and the production build of the React differs from the development version by at least one thing: it's usually minified. So we can try to check if we are working with minified version or not.

To check you can compare function name with the property name of some React object's method, e.g.:

let func = 'Component'
if (React[func].name === func) {
  // non-minified -> development build
}

This method is not about checking production and development but about check the minification, and since production build is usually minified it can really help.

Dolf Barr
  • 509
  • 4
  • 13
  • Sadly, this will result in false negatives because minified !== production. As for me, I tend to use minified code in development. I'd use this only as a fallback for `process.env.NODE_ENV` check. – Estus Flask Oct 16 '18 at 17:47
0

React provides both development and production version of react.js links:

Development:

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

Production:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

source


And to know if it is in development mode or production mode without environment variable, you have to explicitly declare some variable by assigning its mode: (include the following script along with react script)

<script>
var devMode = 'development';
</script>

and check for the devMode whenever necessary.


Alternatively, you may check for its mode like: (you have to add id in the script tag)

var script_id = document.getElementById('script-attached-id');
console.log(script_id.getAttribute('src').includes('development'));

This way, you only need to update the source path and detect the mode.


And the final option, I can think of reading the file itself and detect its mode since react has mentioned in its comment like:

Development:

/** @license React v16.5.2
 * react.development.js

Production:

/** @license React v16.5.2
 * react.production.min.js

So, after reading the file, just check for it's mode in second line. Or you may test for react.development.js without line by line check.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
  • This is true. That's why I specified additional conditions in the question. *some-react-version.js* - it's impossible to figure out which build (development, production) it is from URL. *component that renders differently in production, regardless of modular or global environment* - a component shouldn't be aware of specific ` – Estus Flask Oct 08 '18 at 12:43
  • To make myself clear, I'm asking from developer's perspective, given I have no `devMode` variable to check and have access to `React` and `ReactDOM` objects, not anything else. It could be browser extension I write. Or React library where I need to detect React production mode but don't want to restrict the library to be used in modular environments with process.env. The question is how to detect a build without using process.env. – Estus Flask Oct 08 '18 at 13:03
  • There's no way. You must explicitly set some variable. – Bhojendra Rauniyar Oct 08 '18 at 13:04
  • I hope there's a way to detect this, because production and development builds differ in some way. This possibly requires a good knowledge of React source code which I currently don't have. – Estus Flask Oct 08 '18 at 13:06
0

How does your deployment setup look like? There is no "correct" way to do what you want to do. I would focus on differences between production/development build and create my own helper function.

EDIT: It looks like it is not possible to detect prod/dev version from React class.

Two ideas:

  1. I am not sure how is the application built but PropTypes should be a good ENV identifier.
  2. If your production React application is minified then you can simply detect if the react code is minified. (this will be hacky but it should work, focus on a number of spaces or line length, ... )

I noticed in the comments below that you say that minified !== production if you can make it this way then this is probably your best bet. You don't need to minify development react code anyway.

Michal
  • 4,952
  • 8
  • 30
  • 63
  • I appreciate the suggestion. My deployment setup doesn't matter at this point because the question primarily applies to my code that will work together with somebody else's application. See 'possible uses'. – Estus Flask Oct 16 '18 at 18:05
  • @estus I just updated my answer. I kind of hoped that there will be some `React.method` is that going to be missing in production build but it looks like this is not the case. I would focus on detecting if the react code is minified or not. – Michal Oct 16 '18 at 18:18
  • In 'possible uses' the minification of my own code doesn't matter. For browser extension, minification doesn't affect anything. For component library, it's the setup of a developer who uses my library that matters, not mine. Another answer already suggested minification thing, I commented on that. It's possible to use that but not reliably and this only makes sense in non-modular environment where `React` is global. Yes, I looked for such `React.method` but there's no consistent way. React devtools code linked in another answer shows that it's possible but hacky. – Estus Flask Oct 16 '18 at 18:23
  • @estus Not to pick up a fight here but not using env variables is a hack. :D – Michal Oct 16 '18 at 21:07
  • 1
    As I said, a component can be used in global environment without `process.env`, because in basic setups devs may skip build step. I wanted to figure out my options before hard-coding it to `process.env`. Seems like exposing `productionMode` prop would be a suitable workaround for such cases. – Estus Flask Oct 16 '18 at 21:12
0

Some have touched on using the fact that production will always be minified and development won't be minified. Here's a concrete solution that uses this:

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>

<script>
const reactInfo = {
    version: React.version,
    development: (String((new React.Children.map()).constructor).length > 100)
};
console.log(reactInfo);
</script>

<div id="content"></div>

I've tested this on about a dozen versions of react from v14 to v16. From here you can see that this code hasn't been touched since it was first written, except for one small edit from a year ago (Which wouldn't effect this answer because it's too few characters, though I've tested versions before 11mo ago anyway and there's a decent gap).

Note

Dev is 200 characters and Prod is 70, so there's a 3:1 ratio for a Dev:Prod character ratio. I picked 100 because adding 90 characters of code will add 30 lines to prod, so 100 is the best location with the information given (Technically ~105 or something). Adding 90 characters or removing 100 characters is extremely unlikely for such a simple function (a function that has been touched only once in 5 years with a 20 character edit), so I think this should be stable.

For more stability, or at least knowledge if it breaks, you can check if it's within 25 characters of 70 and 200, and throw an error if it's not. That should catch any big changes (I'd give that option near 100% certainty that it'll never hide a bug), but you might get false positives. Which one you want depends on your use case.

EDIT:

Looking into minification, this is a regex that'll deduce if a function has been minified, so you can use this one safely. It'll also crash if React.Children.map is not a function (Will almost certainly never happen), which you can either catch or not catch depending on how strict you want to handle the unlikely event of an error (Or just ignore it, becuase like, why would they ever change it). The function signature remains, even if it gets minified into [native code], so it's rather future-proof imo. I'd go with the first one for simplicity though.

const reactInfo = {
    version: React.version,
    development: !/function\s?\w?\(\w(,\w)*\)/.test(String((new React.Children.map()).constructor).split("{")[0])
};
Nicholas Pipitone
  • 4,002
  • 4
  • 24
  • 39