4

I'm using node, react, and requirejs, (and generating my AMD-conformant JS from Typescript, but I don't think this is relevant to the problem at hand). I'm simply trying to require React in my app:

// config.js
require.config({
    baseUrl: '../scripts/lib/node_modules',
    paths: {
        lib: '..',
        react: 'react/lib', 
        app: '../../app',
        jquery: 'jquery/dist/jquery',
        scripts: '../..',
    },
});
require(['react/React'], function (react) {

});

Then I'll load this config using RequireJS's data-main api, by navigating to this page:

<!DOCTYPE html>
<html>
    <head>
        <title>My Updated Test Project</title>
        <script data-main="../scripts/config.js" src="../scripts/lib/node_modules/requirejs/require.js"></script>
    </head>
    <body>
        <h1>My Test Project</h1>
    </body>
</html>

RequireJS complains when it tries to load React:

Error: Module name "EventPluginUtils" has not been loaded yet for context: _. Use require([])

In React.js, almost the first line is a "bare require"

var EventPluginUtils = require("./EventPluginUtils");

If I read RequireJS's info about this error message, it says the fix is to stop using bare require.

This occurs when there is a require('name') call, but the 'name' module has not been loaded yet. If the error message includes Use require([]), then it was a top-level require call (not a require call inside a define() call) that should be using the async, callback version of require to load the code

But I don't think changing the react source to use AMD require is what the authors of React had in mind. Are there any other options for this? Do I need to list every module that React.js requires as a shim? That strikes me as a lot of upfront and maintenance work. Am I missing something fundamental about RequireJS? (I'm new to this module).

Edit:

React is packaged as a CommonJS module, so it looks like RequireJS should be able to load it: http://requirejs.org/docs/api.html#packages.

But simply adding the location of the folder containing React's package.json file doesn't seem to get me there. It's trying to load react from the baseUrl as if it didn't recognize that there's a package configured for that module id.

2 Answers 2

3

This question gets a lot of views and I've significantly improved how I work with React in the meantime, so I'll give the current setup.

If you want to use React from RequireJS, it's easy. No packing, custom build steps, or source modification is required. The NPM React module ships with a single script that includes all of React, defined in the UMD format, which is compatible with both AMD and CommonJS. node_modules/react/dist/react.js. (Or pick another flavor, such as node_modules/react/dist/react.min.js). Most NPM modules ship with a dist or build folder that has a single file you can require. This is the correct answer to the original question that I asked.

However, despite the fact that's it pretty easy to combine RequireJS and React, if you are using NPM to manage your front-end JS dependencies, after a lot of work and reflection, I believe that using a packer is a better option for almost all websites. It's far easier and usually higher-performing (in production) to pack dependencies into few files, so that you don't have to perform an HTTP request for every source file that your app wants to use. As a very nice side benefit, you can write your import statements in the easier-to-understand language of CommonJS (see link above), instead of the AMD spec. CommonJS is easier to read and write, requiring less boilerplate at the top of every file.

AMD is designed to be more performant by solving the problem of lazy loading - you don't have to request, parse, compile, and execute, all of the website's code at once. Instead new scripts get downloaded and parsed when the user navigates to a page that needs them. This gets you theoretically faster initial page load, which is a very important metric. However, realistically, there are two reasons why this doesn't hold up. First of all, each HTTP request introduces overhead and latency, it's better to have a few large HTTP requests than a lot of small ones. Even when your site gets large enough so that you really want to break up the code into multiple script files, you still won't want a single script file for every code file in your source. Instead you'll want to pack the code into a few packs, and both packing utilities have some sophisticated options for breaking up your code into packs. Second of all, realistically, a large of the code in your app is going to be required from every page, because it's framework code. The amount of page-specific code that you get to avoid in an AMD system is relatively small compared, until your website becomes very, very large. And yes, I know, RequireJS has a way of combining files for performance. But you have to opt in, rather than out.

There are two downsides to packing that I can see. One is that build times are longer, as the packing code has to go through your entire codebase recursively and parse the code for every require statement, building up a dependency graph, and then it has to write out all of your files into a pack. Webpack has multiple options for improving build time during development (so-called "incremental builds").

The second problem with packing is a bit trickier. The source in your browser is going to be a large packed file.

Both build times and large debugging packs can be solved with careful configuration. For the pack time problem, at a minimum, you'll need to keep large vendor dependencies out of your rebuild. Not only does this improve build times and readability, but it improves caching, since the caching model of a browser is file-based, it's good to separate low and high-volatility code into separate files. In Webpack, you can keep vendor libraries out of your build by declaring them as externals in the Webpack configuration, or you can use the DLL plugin which results in a similar thing, but packs individual vendor libraries into one vendor pack.

Browserify vs webpack? I have used extensively at this point and I recommend webpack. I found it easier to configure (that may be because the documentation is more geared towards my needs) and it has all of the features I wanted, plus a few more for down the road, such as common chunk extraction.

Finally, if you're using typescript, you need to tell it what kind of modules to emit: CommonJS, AMD, or other. You do this by setting tsconfig.json.compilerOptions.module. Valid values include "amd", "commonjs", "umd", and a few others. When using webpack, you want to emit "commonjs". (However, webpack can compile typescript for you as part of its process, so you might want to integrate that step.)

Note on the debate between RequireJS, browserify, and webpack. As it stands today, all three can be configured to produce essentially identical output for almost all use cases. I don't think it was always that way, but it's that way now. The only way to distinguish between them is along non-functional dimensions, such as performance, adoption, and ease of use. For me, webpack was the easiest to set up, and that was the decisive edge. I didn't bother with performance testing.

Sign up to request clarification or add additional context in comments.

Comments

-1

Try using Browserify (npm install browserify) instead of requirejs.I came across this exact problem, but for some reason, requirejs just would not work.

Here's a quick video on Browserify if you're knew to it. It helped me, and I hope it does to you too: http://youtube.com/watch?v=78_iyqT-qT8

1 Comment

You're right, browserify turned out to be the answer. (webpack is an alternative that does the same thing).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.