Organizing a React JS project - building single JS file - reactjs

I just got started using React. I went through the CommentBox tutorial without any issues. But the framework does not give much/any guidance to organizing your JS files or compiling a single minified JS file for a SPA. I already know the framework is flexible and does not enforce a standard and I'm sure these questions are probably dead obvious for someone who develops in the Javascript ecosystem.
I would imagine the consensus is to use Browserify and in the docs there's a link to a git starter project:
https://github.com/petehunt/react-browserify-template
This is a good start, but still it only compiles a single JS file "index.js". I read through some of the browserify handbook and I thought I simply had to 'require' my other files (and those files need to export themselves).
So I modified index.js to look like this:
/** #jsx React.DOM */
var React = require('react');
var pkg = require('./package.json');
var commentBox = require('./comment-box.js');
comment-box.js is basically a hello world test:
/** #jsx React.DOM */
var React = require('react');
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
React.renderComponent(
<CommentBox />,
document.getElementById('content')
);
module.exports = CommentBox;
If I run the react-browserify-template's start target it seems to generate browser-bundle.js fine:
npm start
But if I try the build target
npm build
...nothing happens. I changed the output of npm to verbose and I get the following:
npm info it worked if it ends with ok
npm verb cli [ 'node', '/usr/local/bin/npm', 'build' ]
npm info using npm#1.4.21
npm info using node#v0.10.24
npm verb exit [ 0, true ]
npm info ok
According to package.json it's supposed to generate a file "browser-bundle.min.js" but instead I get no output. I'm hoping someone can clear this up.

I figured out what the problem was. As I stated originally, it is probably obvious for someone who's been developing in the JS ecosystem :)
The package.json in the react-browserify-template has three scripts in the "scripts" section with the keys "start", "build", and "test".
As I said previously, start worked fine by running:
npm start
I wrongly assumbed that I could run the build script similarly:
npm build (this will never work and there will be no errors/output)
It turns out I needed to run the build script using:
npm run-script build
One extra line in the documentation might have saved me hours of trouble :D I'm now using this approach as it seems quite a bit simpler. Also, it sets NODE_ENV to production and uses envify which apparently is important: https://github.com/facebook/react/issues/1772
One other thing, some of the official examples such as todomvc-flux use the lowercase 'react' in the require function:
var React = require('react');
so I suppose that's the recommended syntax (?)

Working solution using Felix's gist. Note: This is not 100% equivalent to the react-browserify-template which uses envify and the production flag to get rid of some React debugging (mainly "Download the React DevTools for a better development experience: http://fb.me/react-devtools" which is printed to the console on startup).
Perhaps a mod can give Felix credit for the solution?
App.js
/**
* #jsx React.DOM
*/
"use strict";
var React = require('React');
var CommentBox = require('./components/CommentBox.js');
React.renderComponent(
<CommentBox />,
document.getElementById('content')
);
components/CommentBox.js
/** #jsx React.DOM */
var React = require('React');
var CommentList = require('./CommentList.js');
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
</div>
);
}
});
module.exports = CommentBox;
components/CommentList.js
/** #jsx React.DOM */
var React = require('React');
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Hello, world! I am a CommentList.
</div>
);
}
});
module.exports = CommentList;

Related

I cannot seem to get React to work

I have been trying to setup webpack, babel, and npm to work with react. I followed the guide on codeacademy - https://www.codecademy.com/articles/react-setup-v. On their last step, they say to create an App.js. Now App.js is a js file, therefore no JSX synthax can be written. This is how my App.js looks:
var React = require('react');
var Component = React.createClass({
render: function (){
return(
<p>Hello World</p>
)
}
});
module.exports = Component;
Now, I know why it throws me an error of unexpected token at line 5 of the code. JS does not recognize the paragraph tag. I tried converting the file to jsx, but that way I cannot import it in my index.js file. I have been trying to setup react for days, and it is very frustrating because I just want to start coding with React very soon.

Gulp with React not compiling a functional component correctly

I have the following gulpfile.js:
var gulp = require('gulp'),
babel = require('gulp-babel'),
concat = require('gulp-concat'),
react = require('gulp-react'),
sass = require('gulp-sass'),
jsxToJs = function() {
//gulp.src('src/**/*.js')
gulp.src('./src/sections/header/header.js')
.pipe(react())
.pipe(babel({
presets: ['es2015']
}))
.pipe(concat('javascript.js'))
.pipe(gulp.dest('./'));
};
gulp.task('jsxToJs', jsxToJs);
gulp.task('build', ['jsxToJs', 'styles']);
gulp.task('watch', function () {
gulp.watch([
'./src/**/*.js',
'./src/**/*.scss'
], [
'jsxToJs',
'styles'
]);
});
gulp.task('default', ['build', 'watch']);
And I'm trying to compile the following functional React component:
let Header = (props) => {
return(
<div />
);
};
However, when I run the javascript.js file created by gulp I get the following error:
Uncaught TypeError: e.render is not a function
If I convert the component back to the old way of doing things like this (which is how I found it as I'm revisiting an old problem):
var Header = React.createClass({
render: function() {
}
});
Then it works.
Looking at the compiled JS shows me this - where I can see that render is being compiled out correctly with the old syntax, but for the new syntax, while it's being ESfivified it's not being reactified:
// not working
"use strict";
var Header = function Header(props) {
return React.createElement("div", );
};
// working
"use strict";
var Header = React.createClass({ displayName: "Header",
render: function render() {
return React.createElement("div", );
}
});
I've checked that I've installed my gulp requires correctly and I'm using Node 6.10.2. My gulp file has some extra things for scss in that I've removed for this question.
A couple of other points:
I'm not using a bundling tool like browserify as I think it's overkill for this project - so no imports or exports.
I'm just loading HTML pages that do JSONP to an endpoint and load a script on page that includes the JSON - this is done in a getInitialState in the page level HOCs.
Can anyone explain what I'm doing wrong?
The solution turned out to be pretty simple.
Babel requires presets to be provided in order to transpile.
I had the es2015 preset, but not the react one. Therefore react specific tranpilations were not occurring. This addition fixed the problem:
.pipe(react())
.pipe(babel({
presets: ['es2015', 'react']
}))
The mistake I was making, that sent me down the wrong rabbit hole in Google, was assuming that failing to reactify was something to do with the gulp-react function - silly me.

Using gulp, browserify and react with addons

I'm trying to learn about gulp, browserify and react and have been knocking up a little test project. This was fine until I decided to implement some animations in there. Specifically this:
var React = require("react");
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
I'm getting an error because "React.addons" is null.
I also have the issue that my build is taking an age - between 20 secs and a minute. I think the reason is partly because react itself is being included in my bundle, whereas I would ideally like to retrieve it from a CDN (or at least keep it separate).
This is my gulpfile:
var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
gulp.task('js', function () {
return browserify('./public/js/app.js', {
debug: false, bundleExternal: true
})
.transform(babelify, {"presets": ["es2015", "react"]})
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('./build/js/'));
});
If I set "bundleExternal" to false then it does stop react being included in my js - but then nothing works because "react" is not found. I found something about browserify-shims but couldn't get it to work from gulp. And wasn't sure if it was the right way to go?
Apologies for the newbie question!
To include ReactCSSTransitionGroup you need to install it first:
npm install react-addons-css-transition-group
Then just require it:
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

TSIFY compatibility with Typescript AngularJS

I am trying to build a gulp based process that would use AngularJS from a bower package and a tool called debowerify (which I believe should let me plug in the installed bower components into the TSIFY/Browserify stream). Essentially, I want to build an app.js file for use in my application (with all the JS I need). I cannot for the life of me get AngularJS to work in this process. I am using an index.ts file (see below) to bring in my references for browserify to work with. It has been tricky with angular as the library is optimized out by the compilation process unless you use the import in some way hence the module call I was testing in the index.ts file.
index.ts
//#### Type Definitions ####
/// <reference path="../../typings/angularjs/angular.d.ts" />
/// <reference path="greeter.ts" />
import angular = require('angular');
import greeter = require('./Greeter');
var greet = new greeter();
greet.sayHello();
angular.module('app', []);
Here are my gulp defintions:
gulpfile.js
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var browserify = require('browserify');
var tsify = require('tsify');
var debowerify = require('debowerify');
var exorcist = require('exorcist');
var config = {
publicDir: __dirname + '/app',
path: __dirname + '/src/typescript',
main: 'index.ts',
result: 'app.js'
};
gulp.task('compile-ts', function(){
var bundler = browserify({
basedir: config.path,
debug: true
})
.add(config.path + '/' + config.main)
.plugin(tsify)
.transform(debowerify);
return bundler.bundle()
.pipe(exorcist(config.publicDir + '/app.js.map'))
.pipe(source(config.result))
.pipe(gulp.dest(config.publicDir));
});
The custom TS files I built (ie: greeter.ts) work great. However, when I run my gulp compile-ts task I get no errors in the console only in the browser when it informs me that angular is undefined (on the call to angular.module). I have suspicions that CommonJS may not be supported for angular, but I also found information on the interwebs to the contrary of that. I am trying to avoid AMD if possible but if that is my only option I would certainly re-consider. I have this application working if I link to the angular script in my html rather than attempt to import it into my typescript, but I am just not satisfied with that approach as I know this should work.
Angular doesn't currently support CommonJS, replacing
import angular = require('angular');
with
require('angular');
solved it for me. See here.

WebPack with React Uncaught Reference Error

I'm very loosely following the example here up until the point where it starts running the dev server.
I have a test React component (in scripts/test.jsx):
/** #jsx React.DOM */
var React = require('react');
var Test = React.createClass({
render: function(){
return <h1>HI!</h1>
}
});
module.exports = Test;
I have a webpack.config where I'm using the jsx loader against the source directory (It's basically the same as the one in the example except I'm adding the library properties).
I run webpack and it generates the bundle file like I expect, however, when I try to use the component in an html file (after including the bundle.js script reference) I get the following in console: "Uncaught ReferenceError: Test is not defined".
The HTML contains the following:
<div id="hi">
</div>
<script type="text/jsx">
/** #jsx React.DOM */
React.renderComponent(
<Test />,
document.getElementById('hi')
);
</script>
Am I doing something incorrect to use a component that is defined in CommonJS style against an html page that isn't technically using a module loader (I'm trying to treat this test as if it's someone who is trying to load the component without any type of structured module loader)?
The output of the webpack is available here
EDIT 2: The full example code is available as a github repo
Yeah, you'd be better off following the example and requiring Test from a .jsx file rather than inlining it in the HTML.
Otherwise, Test needs to be exported as a global, so you'd need to follow similar conventions to the browserify --standalone flag, which looks to be something like this:
output: {
library: "Test",
libraryTarget: "umd"
}
http://webpack.github.io/docs/webpack-for-browserify-users.html#standalone
Edit: After looking at your GH repo, you have:
<script src="bundle.js" type="text/js"></script>
instead of
<script src="bundle.js" type="text/javascript"></script>
so it wasn't loading bundle at all. Further, you can't have 2 separate copies of react, the way you have it currently you're requiring React from within webpack, and then you're also loading it on the page. You should either export a function which takes the React object, or use the global to be able to use it outside of the bundle.
For example this would work:
/** #jsx React.DOM */
module.exports = function(React) {
var Test = React.createClass({
render: function(){
return <h1>HI!!</h1>
}
});
return Test;
};
and then:
/** #jsx React.DOM */
React.renderComponent(
Test(React)(),
document.getElementById('hi')
);
or this
/** #jsx React.DOM */
var Test = React.createClass({
render: function(){
return <h1>HI!!</h1>
}
});
module.exports = Test;
and then:
/** #jsx React.DOM */
React.renderComponent(
<Test />,
document.getElementById('hi')
);
Though I can't imagine most folks consuming a React package are going to be loading it with a script tag, and you generally don't want globals, so it's probably best to just use the CommonJS style require and let people figure out shimming or whatever.

Resources