How to get ReactJs to integrate with Webpack? - reactjs

Basically, I have just been through this simple tutorial, and now I want to get it to use React so I can see that display.
When I had this code:
module.exports = "It works from.js.";
It worked fine. But now I have this:
var React = require('react');
var ReactDOM = require('react-dom');
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
and it is complaining at line 9 point at the unexpected token < and I need an appropriate loader. anyone know what this loader may be?
thanks

Yeap, you are right you need babel-loader for your JSX code. Also if you use ES6 syntax with React you also need babel-core & babel-preset-es2015 & babel-preset-react npm's modules. After that incude all of them into your webpack.config.js file as below:
module.exports = {
//.. some stuff before
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loaders: ["babel"],
query: { presets: ['es2015','react'] }
}]
}
//.. another stuff
}
Also i leave a link for you, with tutorial how to use ise.
I hope it will help you.
Thanks

Related

AngularJS template loading late. Directive not able to find the elements

I'm working on preparing a .net based AngularJS web application for modern tooling(get rid of nuget in favor of npm, bundling with webpack, etc.) and later on re-writing it to Angular.
I'm having an issue with the bundled version where a directive is trying to bind a click events to a template anchor tag (< a >) but the template is not yet loaded.
On the old version with many < script > tags for every JS file this is not happening. The directive is first on the order and the controller of the template(which is loaded inside a ng-include and uses the directive) comes after.
On the bundled version I simply changed the .js files to .ts, added the npm dependencies, the needed imports statements on each file and in the webpack entry I kept the same order as in the old index.html. Still, when de directive code runs the elements it searches for are not there yet.
The parts affected: (already updated with #bryan60 answer suggestion)
shell.html
...
<div data-ng-if="vm.showMenuBar" data-ng-include="'/app/layout/sidebar.html'" class="fader-animation"></div>
...
shell.ts
import angular from 'angular';
const sideBarTemplate = require('./sidebar.html')
let controllerId = "shell";
angular.module("eqc").controller(controllerId, ["$rootScope", "$templateCache", "$window", "authService", "common", "config", shell]);
function shell($rootScope, $templateCache, $window, authService, common, config) {
$templateCache.put('app/layout/sidebar.html', sideBarTemplate)
sidebar.html
<div data-cc-sidebar data-ng-controller="sidebar as vm">
<div class="sidebar-filler"></div>
<div class="sidebar-dropdown">Menu</div>
<div class="sidebar-inner">
<div class="sidebar-widget"></div>
<ul class="navi">
<li class="nlightblue fade-selection-animation" data-ng-class="vm.isCurrent(r)" data-ng-repeat="r in vm.navRoutes">
</li>
</ul>
</div>
cc-sidebar directive:
app.directive('ccSidebar', function () {
// Opens and clsoes the sidebar menu.
// Usage:
// <div data-cc-sidebar>
// Creates:
// <div data-cc-sidebar class="sidebar">
var directive = {
link: link,
restrict: 'A'
};
return directive;
function link(scope, element, attrs) {
var $sidebarInner = element.find('.sidebar-inner');
var $dropdownElement = element.find('.sidebar-dropdown a');
element.addClass('sidebar');
$dropdownElement.click(dropdown); // <--- Here's the problem. 'dropdown' function is omitted but its defined in the next line
// The error on the console is: TypeError: "$dropdownElement.click is not a function"
// That's because it is never found
main.ts
import "./eqc";
import "./config"
import "./config.exceptionHandler"
import "./config.route"
//All folders bellow have index.ts in them including their .ts files
import "./common"
import "./services"
import "./Views"
import "./layout"
eqc.ts
import 'jquery';
import angular from 'angular';
import 'angular-animate';
import 'angular-route';
import 'angular-sanitize';
import 'angular-messages';
import 'angular-local-storage'
import 'angular-ui-bootstrap';
import 'angular-ui-mask';
import 'angular-loading-bar';
import 'breeze-client';
import 'breeze-client/breeze.bridge.angular';
let eqc = angular.module("eqc", [ .....
webpack.config.ts
const path = require('path');
module.exports = {
mode: "development",
entry: "./src/app/main.ts",
output: {
path: path.resolve(__dirname, "src/dist"),
filename: "bundle.js"
},
module: {
rules: [
//All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'
{
test: /\.tsx?$/,
use: { loader: "ts-loader" }
},
{
test: /\.html$/,
use: { loader: 'html-loader' }
}
]
},
resolve: {
//Add '.ts' and '.tsx' as a resovaable extension
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
devtool: "source-map"
};
My folder structure:
not sure exactly what your problem is due to the lack of code / details... but very generally, the simplest way to make ng-include work with webpack is to use require statements and the template cache....
Assuming you have some template like:
<ng-include src="'app/my-included-template.html'"></ng-include>
in the controller for that template, you'll have something set up like...
const myIncludedTemplate = require('./my-included-template.html')
function MyController($templateCache) {
$templateCache.put('app/my-included-template.html', myIncludedTemplate)
}
to make the require statement work with webpack, you'll need am html loader configured, i have this to do it in my module rules array...
{
test: /\.html$/,
use: [
'html-loader'
],
},
this particular implementation will require you to npm install --save-dev html-loader
this will instruct webpack to inline your template to the source file, and then your controller will immediately put it into the template cache so that it can be loaded correctly without worrying about the webpack bundle structure itself as angular will check the template cache before loading remotely. This is also a generally more performant way of doing things.
you can also do something similar when defining your directive / component templates:
template: require('./my-directive-template.html'),
which yields the same benefits of webpack just inlining the template so it doesn't need to be loaded remotely.

React npm package: Do I need a different webpack config to be ssr safe?

I maintain a small npm package and I am currently trying to use it in a Gatsby site. I am not a webpack expert and I am struggling to make my npm package ssr safe.
Currently, if I try gatsby build it will throw the following error:
failed Building static HTML for pages - 0.829s
WebpackError: ReferenceError: window is not defined
componentDidMount = () => {
window.addEventListener( 'click', this.onClickCloseMenu, false );
}
I added the following file to my package:
// allows us to use window server-side
const safeWindow = (typeof window === 'undefined') ? {
addEventListener() {},
removeEventListener() {},
} : window;
export default safeWindow;
and imported the object in my code:
import safeWindow from './safeWindow';
...
componentDidMount = () => {
safeWindow.addEventListener( 'click', this.onClickCloseMenu, false );
}
But unfortunately this didn't help. I was already using window only in useEffect/componentDid(Un)Mount but I guessed since it was a compilation error that I needed to define it. My next guess is that building/minifying my package to publish it to npm broke this again but I am not sure.
This is my webpack config:
var path = require('path');
module.exports = {
mode: 'production',
entry: './src/DataListInput.jsx',
output: {
path: path.resolve('lib'),
filename: 'DataListInput.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules)/,
use: 'babel-loader'
},
{
test: /^(?!.*?\.module).*\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.module\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true
}
}]
}
]
}
}
Any help is highly appreciated!
Find the npm package here
UPDATE
I am pretty sure now that it has something todo with webpack bundling css together with javascript in the minified file which requires calls to both window and document. Does someone has experienced something similar? How do I solve this issue?
UPDATE 2
I resolved the issue by removing the css. I created a new ssr safe npm package without css. It feels more like a work-around than a solution, so I am still interested if there is a way to bundle css + javascript and be ssr compatible.
Changing safeWindow from a constant into a getSafeWindow function should fix it.
A constant is evaluated before the module can be imported. While a function version will only access window when it is called at runtime.
const getSafeWindow = () => (typeof window === 'undefined') ? {
addEventListener() {},
removeEventListener() {},
} : window;

How to use react as a component library rather than application

I'm trying to learn react and also introduce it in already existing project. Thus I want to use react in a few places where it makes sense, instead of using it everywhere.
The problem is the entry-point concept that I can't wrap my head around. I'd prefer to have:
ReactDOM.render(
<MyComponent/>,
document.getElementById("componentExample")
);
inside my cshtml near <div id='componentExample'></div> rather than in a separate jsx. I understand that I can't use jsx syntax in cshtml, but if I write
<script src="#Url.Content("~/Scripts/dist/bundle.js")"></script>
<script>
ReactDOM.render(
React.createElement(MyComponent, {}, null), document.getElementById("componentExample")
);
</script>
This doesn't really work because MyComponent is undefined. It is included in my bundle.js (I had to add it as entry point), but it is inside the scope of some webpack stuff and is not exposed.
What is the proper way of doing this?
My goal is to write some components for my app where it is more suitable and use it here and there, without completely going into react-based architecture.
My setup is webpack + babel polyfill + typescript + react and this is in ASP.NET MVC.
My webpack.config.js is:
var config = {
entry: ['babel-polyfill', "./SiteScripts/React/index.tsx", "./SiteScripts/React/Components/MyComponent.tsx"],
output: {
filename: "./Scripts/dist/bundle.js",
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
rules: [
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{
enforce: 'pre',
test: /\.js$/,
loader: "source-map-loader"
},
// All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'.
{
test: /\.tsx?$/, loaders: ['babel-loader', "ts-loader"], exclude: /node_modules/
}
]
},
plugins : []
}
if (process.env.NODE_ENV === "production") {
config.devtool = "cheap-module-source-map";
}
module.exports = config;
Also this is somewhat related to the question (as question about architecture): I'd like to make my bundle.js smaller - it is 30k lines now. It is probably because I'm trying to use react in a wrong way?
I finally found the problem. I had a few mistakes (and didn't completely understand the whole theory behind webpack) and the answer is definetely beyound the code I gave in the question - so I do not have any answers.
First I had to make by bundle a library, in order to export stuff defined in it (webpack.config.js):
entry: ['babel-polyfill', "./SiteScripts/React/index.tsx"],
output: {
filename: "./Scripts/dist/bundle.js",
libraryTarget: "var",
library: "WebPack"
},
This will make types/variables defined in index.tsx (I think only last file from entry list is taken) and that are exported - available via WebPack.<name>
I needed to export my component. I've done it like this - but there are probably better ways (index.tsx):
import { MyComponent} from "./Components/mycomponent";
export var myComponent = MyComponent;
I also needed to ensure all my subcomponents are modules. This means using import everywhere, and adding a keyword export to all element, props and states.
After that I was able to render my component from:
<script src="#Url.Content("~/Scripts/dist/bundle.js")"></script>
<script>
ReactDOM.render(
React.createElement(WebPack.myComponent,
{
PropA = 2, PropB = "3"
}, null),
document.getElementById("componentExample")
);
</script>
One more thing to watch for if I am doing this is that the line <script src="#... does not happen more than once in my result html (this is very likely as I am using many components embedded in a single page). I use this javascript function to ensure I load bundle.js only once:
function loadJsOnce(url, whenFinish) {
if (!window.loadedScripts) {
window.loadedScripts = {};
}
if (!window.loadedScripts[url]) {
var script = document.createElement('script');
//script.async = true; // remove this if you don't want the script to be async
script.src = url;
script.onload = function() {
whenFinish();
}
document.getElementsByTagName('head')[0].appendChild(script);
window.loadedScripts[url] = true;
} else {
whenFinish();
}
}
In url I specify the path to bundle.js and in whenFinish function I put a function that calls ReactDOM.render(... stuff

Illegal import declaration with gulp-react

I'm trying to introduce React with ES6 into my app, which is built with Gulp. I can get React working alone, and ES6 working alone. But trying to import crashes my Gulp build. Gulp's error is:
Line 14: Illegal import declaration
My HTML page includes React and the built JS file:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/react.min.js"></script>
<script src="js/app.js"></script>
app.js looks like this:
import {Child} from './child.js';
var Parent = React.createClass({
render: function(){
const y = 3; //es6 works
return (
<div>
<div> This is the parent {y}. </div>
<Child name="child"/>
</div>
)
}
});
React.render(<Parent />, document.getElementById('container'));
child.js is in the same directory and looks like this:
var Child = React.createClass({
render: function(){
return (
<div>
and this is the <b>{this.props.name}</b>.
</div>
)
}
});
export default Child;
The part of my Gulp file that compiles ES6 with Babel:
gulp.task('babel', function(){
gulp.src('js/app.js')
.pipe(react())
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist/js'));
});
What am I missing/doing wrong that is preventing me from using import?
You have to apply the es6module option to gulp-react to allow for importing of modules:
.pipe(react({
es6module: true
}))
Then, fix your import statement. Since Child is exported as default, import it like this:
import Child from './child.js';
Note: gulp-react was deprecated in favor of gulp-babel so I suggest using that instead if you're already using it for es2015. use gulp-babel instead and install the react preset for Babel:
npm install --save-dev babel-preset-react
Finally, apply it to gulp-babel:
presets: ['es2015', 'react']
Try
import Child from './child';

Bootstrap Material Design with Webpack

perhaps a really rookie webpack question, but here i go:
Was wondering (since i really havent found any direct answers) if anyone could give me a hint on how to get bootstrap material design working? I get the css stuff going for me but not the ripples.js stuff...
my webpack setup, well bits of it:
...
plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ],
module: {
loaders: [
{ test: /\.js?$/, exclude: /(node_modules|bower_components)/, loader: 'babel' },
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
{ test: /\.(woff|woff2)$/, loader:"url?prefix=font/&limit=5000" },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" }
]
}
...
in my react's index.js which is where it all starts:
...
import Bootstrap from 'bootstrap/dist/css/bootstrap.css'
require('bootstrap-material-design/dist/css/bootstrap-material-design.css')
require('bootstrap-material-design/dist/css/ripples.min.css')
...
how would i get the material.js and ripples.js to work?
i get no console errors or webpack errors, but the darn ripples are not showing! i am guessing there is a smart webpack way to get this all running or do i need to require the .js explicitly in my index.js (that has not worked)?
thanks in advance,
hanto899
UPDATE:
if i require the following in my index.js file:
require('bootstrap/dist/css/bootstrap.css')
require('bootstrap-material-design/dist/css/bootstrap-material-design.min.css')
require('bootstrap-material-design/dist/css/ripples.min.css')
require('bootstrap')
var material = require('bootstrap-material-design/dist/js/material.js') // not sure if the "var material =" is necessary here...
var ripples = require('bootstrap-material-design/dist/js/ripples.js') // not sure if the "var material =" is necessary here...
let $ = require('jquery')
$.material.init()
i get no errors, but ripple doesnt work. but... if i navigate to another page, and add, while on that page, :
let $ = require('jquery')
$.material.init()
webpack hot-reloads and then i get the ripple working. howerver if i refresh the page, i get:
Uncaught TypeError: Cannot read property 'init' of undefined
....
Finally, I got it working.
You can simply import them as follows
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-material-design-master/dist/css/bootstrap-material-design.css";
import "bootstrap-material-design/dist/css/ripples.css";
import "bootstrap-material-design-master/dist/js/material.js";
import 'bootstrap-material-design-master/dist/js/ripples.js';
Then you need to call
$.material.ripples()
$.material.ripples() will apply ripples.js to the default elements.
Above answer by #MrJSingh and #Rob Kielty was not working for me.
So, I did some necessary modifications. Hope this will help.
In your entry point file (i.e. index.js in my project), import the files as follows
import '!style-loader!css-loader!bootstrap/dist/css/bootstrap.min.css';
import '!style-loader!css-loader!bootstrap-material-design/dist/css/bootstrap-material-design.min.css';
import '!style-loader!css-loader!bootstrap-material-design/dist/css/ripples.min.css';
import 'bootstrap-material-design/dist/js/material.min.js';
import 'bootstrap-material-design/dist/js/ripples.min.js';
Then call
$.material.init();
Find my webpack configuration is here

Resources