Building React with ES6 imports in traditional HTML/CSS/JS environment? - javascript

I have a big old website that I am adding react components to. It uses node/express and handlebar templates mostly. Basically I do it like this:
The site imports react libs in the old way (in an html file):
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
And then I use it like this:
HTML:
<div id="react-container"></div>
<script src="react-component.jsx" type="text/babel"></script>
react-component.jsx:
const Component = ()=>{ ... }
const container = document.getElementById("react-container");
ReactDOM.render(React.createElement(Component), container);
The issue is if I want to import libraries, they have to be available from CDN via script tags. I have found a couple now that aren't and also it would be nice to be able to see what I'm importing at the top of a file instead of just having a bunch of libraries floating around on the window object.
Anway, I can't use create-react-app but I'm wondering how I can go about inserting a small build step into my system to make it possible to npm/yarn install libs and then import them into my code.
Thanks.

I am not familiar with the technologies you're using but I thought your question was interesting and I looked a bit into it.
Here is what I would try to do:
Since you're using node you could use Webpack to bundle each of your react components in its own separate file (https://webpack.js.org/concepts/output/#multiple-entry-points) and tell Webpack to put the generated files in a folder that can be served by express. (https://expressjs.com/en/starter/static-files.html). Webpack will take care of bundling all the dependencies that you might install using npm. You just need to expose the component to the window by exporting it so that it can then be accessed by step 2.
You could then use a <script> to load the component bundle you need in a specific template and then render it using ReactDOM.render(React.createElement(YourComponent), document.getElementById('the-container')).
If you're not familiar with Webpack you can take a look at this article: https://www.valentinog.com/blog/webpack/#how-to-set-up-react-webpack-5-and-babel-from-scratch
I did not test this so I'm not sure it works but as I said I thought it was an interesting question and I want it to give it a go and maybe give you some useful ideas.

Related

StencilJS using component in HTML file

I want to use a component created using StencilJS in a regular basic HTML file. I followed these steps:
I have created a stencil component to create the basic my-component example:
npm init stencil
I want to use this component in an HTML file, so I ran
npm run build
I then created an html project with the following structure:
And then I moved the files from the dist folder into the script folder. And I added script tag in the head of my html file that references the component.js file like this:
<script src="script/{component_name}/{component_name}.js"></script>
And I used the component in the html like this:
<my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>
But my component isn't being rendered. I get an error involving a esm.js file. Can someone help me with this process of compiling my stencil component to be using in a basic HTML project?
Stencil bundles your dist into modules and lazy-loads only the required code based on the components you are actually using in your HTML. So you should serve the whole dist folder along with your website.
The recommended way is to have the following two script tags in your html file:
<script type="module" src="/dist/[namespace]/[namespace].esm.js"></script>
<script nomodule src="/dist/[namespace]/[namespace].js"></script>
(where [namespace] is whatever is set in your stencil.config.ts)
This will instruct browsers who support ES Modules to use the esm bundle, and other browsers will use the ES5 (cjs) bundle.
If my-component is the only component that you're using from your library, then only that code will be lazy-loaded by your page. Stencil knows about component interdependencies and how to lazy-load them accordingly.
There is a new experimental output target (called custom-elements-bundle) that allows you to bundle everything into one js file, which will simplify distribution in some cases. It's only available with the new refactored compiler (which is available using the --next flag, after installing #stencil/core#next) (Stencil 2 has been out for a while now).

How to properly reference the path with newly installed packages using npm in Laravel?

I've installed ReactJS and Babel using npm in my laravel project. They are found in "node_modules" folder created by npm itself. But the problem is my react code doesn't seem to work if I import using this code:
<script src="node_modules/react/react.js"></script>
<script src="node_modules/react-dom/dist/react-dom.js"></script>
<script src="node_modules/babel-core/lib/api/browser.js"></script>
Is the format of the path correct? because if I just import using this type of code for all my imports required for ReactJS:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
It seems to work if I just use the internet for getting the packages.
My react code is here:
<div id="myapp"></div>
<!-- JavaScripts -->
<script type="text/babel">
var BuckyComponent = React.createClass({
render: function() {
return(
<h2>{this.props.user} likes to eat {this.props.food}</h2>
);
}
});
React.render(
<div>
<BuckyComponent user="Arth" food="Bacon"/>
<BuckyComponent user="Arth" food="Pork"/>
<BuckyComponent user="Arth" food="Chicken"/>
</div>,
document.getElementById('myapp')
);
</script>
That's not the way you use Npm/Bower packages within Laravel. Either package manager will store its files outside the Laravel public folder, the first inside node_modules and the later inside bower_components. So they're not intended to be publicly available.
So how would you use their packages within Laravel? Using build systems like Laravel Elixir, just the Gulp or even the Grunt.
It's all up to you to copy the distribution files of React and Babel, concat them, minify them and the like. You'll end up with them inside the public folder, but before you'll want to do a nice concatenation and minification of all that stuff to serve the smallest possible number of files and their sizes.

how to use webpack to load CDN or external vendor javascript lib in js file, not in html file

I am using react starter kit for client side programming. It uses react and webpack. No index.html or any html to edit, all js files. My question is if I want to load a vendor js lib from cloud, how to do I do that?
It would be easy to do that in a html file. <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
However, in js file, it only uses npm installed packages. How can I import the above lib with no html file? I tried import and require, they only work for local files.
update 10/21/15
So far I tried two directions, neither is ideal.
#minheq yes there is a html file sort of for react start kit. It is html.js under src/components/Html. I can put cloud lib and all its dependencies there like this:
<div id="app" dangerouslySetInnerHTML={{__html: this.props.body}} />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>
<script src="/app.js"></script>
<script dangerouslySetInnerHTML={this.trackingCode()} />
</body>
Good news is it works, I don't need do anything else in js file, no import or require. However, now I have two jquery libs loaded in different ways. One in here, the other through npm and webpack. I wonder it will give me trouble later. The react-routing I use give me 'undefined variable' error if I type a none home path in browser window due to the server side loading I guess. So this solution is not very good.
Use webpack externals feature. This is documented as: link. "You can use the externals options for applications too, when you want to import an existing API into the bundle. I.e. you want to use jquery from CDN (separate tag) and still want to require("jquery") in your bundle. Just specify it as external: { externals: { jquery: "jQuery" } }."
However, the documentation I found a few places are all fussy about how to do this exactly. So far I have no idea how to use it to replace <script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script> in html.
externals is not intended to let you do this. It means "don't compile this resource into the final bundle because I will include it myself"
What you need is a script loader implementation such as script.js. I also wrote a simple app to compare different script loader implementations: link.
var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", function() {
$('body').html('It works!')
});
You can create a script tag in your JS as
$("body").append($("<script src="https://forio.com/tools/js-libs/1.5.0/epicenter.min.js"></script>"))
There is one html file that is definitely being used to serve to users with your js bundle attached. Probably you could attach the script tag into that html file
Use webpack's externals:
externals allows you to specify dependencies for your library that are
not resolved by webpack, but become dependencies of the output. This
means they are imported from the environment during runtime.
I have looked around for a solution and most of all proposals were based on externals, which is not valid in my case.
In this other post, I have posted my solution: https://stackoverflow.com/a/62603539/8650621
In other words, I finished using a separate JS file which is responsible for downloading the desired file into a local directory. Then WebPack scans this directory and bundles the downloaded files together with the application.

Using requireJS to initiate React component in Symfony2 template

I am having trouble getting React components to work in my Twig templates in Symfony2, using RequireJS to initiate them. I was trying to get this one to work: https://github.com/rackt/react-autocomplete
First I installed it, locally, in parallel to where I have my css files for my templates:
npm install react-autocomplete
Then, some selected parts from my twig template:
<html>
<head>
<script src="https://fb.me/react-0.13.1.js"></script>
<script src="https://fb.me/JSXTransformer-0.13.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.17/require.min.js"> </script>
</head>
<body>
<div id="react_demo">
<script type="text/jsx">
require(["{{asset('/bundles/demobundle/js/node_modules/react-autocomplete')}}"], function (ReactAutocomplete) {
alert('react-autocomplete loaded');
});
</script>
</div>
</body>
</html>
And now, to the problem: it doesn't seem to find my React component - or doesn't understand that it is a React component I'm trying to load... for some background info I haven't done anything except for including the React and Require JS files, and install the component. Perhaps I've missed something?
Uncaught Error: Load timeout for modules: /bundles/demobundle/js/node_modules/react-autocomplete
That one looks like a CommonJS module and while those can be loaded with RequireJS it needs some additional configuration.
While the packages can have the CommonJS directory layout, the modules themselves should be in a module format that RequireJS can understand. Exception to the rule: if you are using the r.js Node adapter, the modules can be in the traditional CommonJS module format. You can use the CommonJS converter tool if you need to convert traditional CommonJS modules into the async module format that RequireJS uses.
Also see http://requirejs.org/docs/commonjs.html and https://stackoverflow.com/a/16522990/1630906

Use grunt to switch HTML elements when switching from development to production environment

I would like to have a dev and a production setup in grunt. I have figured out all of the issues with concatenating, linting and compressing my JS for build and beautifying my libraries during development.
What I would like to do is be able to seemlessly switch my Js source at the app entry point.
I have
<!--<script data-main="js/main" src="js/libs/require.js"></script>-->
<script src="js/main-built.js" ></script>
and currenntly I am manually switching the comments when I switch environments. I would like to automate this.
You're looking for the grunt-targethtml plugin, which allows you to use different assets for different targets.
<!--(if target dev)><!-->
<script src="dev.js"></script>
<!--<!(endif)-->
<!--(if target dist)>
<script src="release.js"></script>
<!(endif)-->
On the same topic, if you only want to change some configuration options in the Javascript you're using, there is no need to use a different source file: you can use the grunt-replace plugin.

Categories

Resources