dynamically require/import different component modules in React Native depending on build config - reactjs

I'm working on a React Native project where we will need to build two different flavours of the app, which mostly the same but vary in a few small features. I wanted to see if there was a way to do this through babel somehow, by having different files named similarly for each build and triggering differetn builds by setting and environment variable. This would be similar to the way that React Native does for having custom js files for iOS and Android like this:
my-component.android.js
my-component.ios.js
so my different build flavours would look like this:
my-component.flavourA.js
my-component.flavourB.js
or even
my-component.flavourA.android.js
my-component.flavourB.android.js
my-component.flavourA.ios.js
my-component.flavourB.ios.js
I am trying to find a way of using babel to change the require() function (and import statements) such that the correct files are resolved from a simple require call of:
require('./my-component');
depending on which FLAVOUR environment variable is set. Or:
import * from './my-component';
I have looked at trying to use a combination of babel-plugin-module-resolver, babel-plugin-replace-require and babel-preprocessor but I'm not able to work out if this even possible without writing my own babel plugin.
Is there an easier way of achieving this that I am missing?

Related

What is the difference between importing a package after installing it or including it in index.html when a new react application is created?

When, we use create-react-app while creating a new react application. It creates multiple files. Out of which index.html is the rendered html application, where multiple jsx elements are placed depending upon the react application App.jsx
I was curious on what is the best way to import google fonts, bootstrap, jquery and other different external plugins?
As I have researched there are two ways of importing external modules. For eg. if we consider bootstrap that is to be imported from cdn: https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js
Then, one way to import is place it in public/index.html:
# Rest of the index.html code
<div id="root"></div>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
# Rest of the index.html code
Other way is to install bootstrap using npm i bootstrap#4.3.1 --save, and then put it as import in src/index.js .
The above thing can be applied to multiple places. Say, we have to import popper.min.js or jquery.js or import css fonts. Which is better? Placing it in index.html or inside index.js?
Also, what is the major difference between placing it at the two different places?
Both approaches will have the similar effect of making that library available to your application.
The HTML approach causes your dependencies to be fetched and executed at run-time by the browser. Much like you'd include imgs on a page, you can include scripts on a page with the source of the script either inline or from another URI. So, let's say you create an HTML page, put a number of script tags around it, and then hit that page in your browser. Your browser will scan the HTML page, identify all script tags and start downloading them. It will also remember in which order the script tags were found, and it will parse and execute them in that order (unless you use async and defer attributes on them).
Ok, so, browser sees HTML page consisting of various elements, including some script tags, download them (probably in parallel), and execute them (sequentially). Now, these scripts may or may not know anything about each other on the page.
Now, let's go to the realm of React and other rich Javascript apps, which depend on various Javascript libraries.
When you do Javascript Module Bundling i.e. import { something } from "package", this will get picked up at compile time by your Javascript compiler, e.g. React via create-react-app when you do npm run build, or Angular's equivalent script, or other compilers such as webpack, etc. These compilers will scan not just a single file, but rather your entire application. They typically start from an entry point, e.g. index.jsx, and then discover the graph of dependencies and recursively go through each file or module that they identify and discover those dependencies and so on and so forth. Once the compiler is done discovering, resolving, building, and bundling your app, you'll typically end up with a single Javascript file (e.g. main.[some hash].js) which is your application AND all dependencies (all those other modules you imported) bundled together in a single file.
So, you see the big distinction is:
HTML scripts are independent resources that are downloaded and processed by the browser at run time.
Javascript modules are discovered at compile time and end up being bundled together with your application code in a single file.
I'm leaving aside concepts such as package splitting and dynamic references, etc. in order to illustrate the larger distinction; you'll end up reading about variations in each approach as you dig further into them.
HTML Script advantages:
They are fetched independently, perhaps from a CDN, may even already be cached in your browser if another website needed them and downloaded them previously. So things like jQuery, loDash, etc. are common packages that may have already been downloaded, and your browser will benefit from its internal cache.
They can be downloaded in parallel; although you can rely on the browser to ensure that they get executed sequentially. So for example if you had your own script that relied on jQuery having already been loaded, all you need to do is to <script src="jquery.min.js" /> first and then <script src="myscript.js"> and that sequence will be respected.
Javascript Module Advantages
Your code may not need the entire jQuery, lodash, or whatever other library that you're referencing. By importing whatever function of your dependencies that you need in your source code, a smart compiler might be able to artfully scalpel only those functions out of the larger library (tree-shake the library), and you'll end up with a smaller overall download payload.
Your entire bundle can be minified, yielding an optimized package.
Your entire bundle will be (/can be) in a single file. One download, and your entire app is loaded and ready to use. No need to worry about downloading various resources from various URLs.
Hybrid Approach is OK
Feel free to use a hybrid solution! If you inspect your compiled React code, you'll see that the create-react-app compiler will inject a <script src="static/js/main.js" /> element at the end of your HTML document. This means that your app can rely on other`s included higher up in your HTML document. So, feel free to load up some libraries in HTML and reference other ones through JS modules. In fact, there are cases that you'd want to do this; for example, including the Google Maps script can be easily done via an HTML script directive, and your React app can still use the GMaps library.

Micro-Frontends, Web Components, and Sharing Libraries

so I'm working on migrating my company's app to a micro-frontend approach. We are following the standard described in https://micro-frontends.org/. While under the hood everything is currently using React, we are wrapping things with Web Components so that we will have the freedom and flexibility to be framework-agnostic in the future. We've got a working architecture up and running and so far it is working beautifully. We even created a fancy compatibility layer on top of the Web Component spec which allows us to pass React-like props to the Web Components, including Objects, Arrays, and even Functions. This allows for much better interaction between them.
The main concern we have right now is duplication of libraries. We're a React shop, so even though we have this framework agnostic approach, everything is using React. While this new approach gives us the ability to individually upgrade pieces of our app to a newer React version (finally), we still don't like the idea of so much duplication of the React library.
To put it in perspective, even Gzipped, React/ReactDOM are over 40kb. That's super tiny individually, but scaled up it starts to take up more and more bandwidth. RAM-wise it's less of an issue, about 130kb for those libraries, and given the RAM capacity of most devices now it's not a huge deal.
But, of course, we want things to be as optimized and streamlined as possible. So I'm hoping someone can suggest a way for the micro-frontend apps (the ones wrapped in a Web Component) can get React and other libraries from the parent app.
You should know that the parent app JavaScript is loaded prior to the micro-frontends. Each micro-frontend is loaded via a <script> tag. Lastly, we are NOT using the Shadow DOM at the moment, a tradeoff we made to benefit how we are migrating our existing code into the new micro-frontend architecture.
The core idea is to tell module bundler on how to package your micro-frontends.
Assuming you are using Webpack to bundle your applications, here are the two things that you need to do.
Step 1:
Declare React as an External dependency like in your Webpack config:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
Step 2:
Before you load your parent application's JS, ensure that you are loading React and ReactDOM from CDN or other equivalent place:
<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>
Put these script in your main index.html which is responsible for bootstrapping your entire SPA.
Explanation
When you declare certain package/library as external, Webpack does not include it as part of the bundle. It assumes that the outer environment will make that particular version available as a global variable. In case of React, it uses React and ReactDOM as global variables.
By doing this and including it via CDN, you will be left with exactly one copy of React and ReactDOM. When a user visits the application for the First time, it will be slower but once cached, it should not be a problem
Further, you can extend this idea and also declare them as external for your parent app or parent shell container.
Possible solution is to prepare library using Import Map but as it does not support IE11+ I recommend you using SystemJS?
https://github.com/systemjs/systemjs
especially this one seems to be close to your case:
https://github.com/systemjs/systemjs-examples/tree/master/loading-code/react-hello-world
At html you do:
<script type="systemjs-importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.min.js"></script>
Then you could import ReactJS, as browser knows where to take it from.
Potentially there is a possibility to build some kind of library that looks into your micro front end's (MFE's) package.json, to get know which library needs to be in use and dynamically create imports object similar to example above.
We need to keep in mind that there is a need to cover versioning check. Mentioned library could potentially store some kind of map which connects dependency with version with place where it's accessible. Imagine that in case mentioned we need to deal with different react version on each MFE :).
Then while loading another MFE library could check if required dependencies has been already included, if some missing, download it, otherwise use what has been already fetched.
Another thing is use webpack 5, with module federation, which I not recommended yet, as it is not stable enough for production, but there is a possibility to start playing with it. Hard part will be covering version check, so most probably need another abstraction layer to deal with it.
https://indepth.dev/webpack-5-module-federation-a-game-changer-in-javascript-architecture/

Different branding for same codebase

Im in a project where we will create different sites using the same codebase.
I would like to have a brand style and config for each site which I specify somehow in my build process.
Anyone have an idea of the best way to achieve this ?
I would treat the different sites in much the same way I'd treat different environment (dev, test, prod). If there aren't a lot of changes, just use environment variables on each server where the site will run that define which site it is. Your code can then conditionally do things (e.g. add a class site-x to the body for styling).
You can use something like dotenv to make setting environment vars easier (remember Windows does it differently to *NIX) if you're setting environments in a script. That way you're changing a file rather then actual environment variables when you want to test what a particular site looks like.
If there are many different config items that are different between sites then you can have multiple config files (config-site-one.js, config-site-two.js) and a central config.js file that returns the correct config based on some environment variable like MY_SITE_NAME.
However if you actually want to package up the site to 'send' somewhere (?) then you could run your build command with a flag like webpack blahblahblah --site=site-one.
You can use yargs to get that 'site' variable and use it in your build process however you like.

How do you manage several ReactJS projects?

Background: My company's web-app is in fact several completely separated apps that have minimal effect on one another. That's why it doesn't make any sense to keep them under a single project - so I'm using separate projects. However, since I started migrating and writing new apps in ReactJS (using Webpack for bundling) I find many opportunities for reuse, in order to avoid code duplication and to avoid downloading common resources again after another app already fetched them:
I'm using specific versions of 3rd party npm modules - so no reason to bundle them over and over again - and no reason to upload them to the server more than once (for all the projects).
How do you share package.json fixed versions across projects?
Do you bundle all/groups of npm modules together or each separately? I want to get everything from the server for cases of on-premise deployment.
What measures to you take to make sure you don't forget to re-bundle the node modules in case you decided to update one of them?
Similar question goes for font files (keeping the font on the server and another font for icons)
common style sheets
common JS code - like utils & common
common web.config configurations
common images (currently some of them are inline in different bundles)
and last - common components - how do you share them? Do you use a separate project and publish to npm? symlink?
Or maybe, after all, I should go with a monorepo? And how?
You could create a separate project, my_modules, which is just a manifest of the shared, common packages. You'd have a package.json with all the common modules, and a single index.js which does something like:
import React, { Component, PropTypes } from 'react';
import moment from 'moment';
export { React, Component, PropTypes, moment };
Then, you'd publish this new my_modules project to github, and install that in your other projects.
import { React, Component, PropTypes } from 'my-modules';
Every time you change a package, you'd increment the version, and then update that version in your projects (which is still a bit of a pain, but you could use something like greenkeeper.io to ensure they don't get stale).
Similarly, you can do the same thing for just about everything else you've mentioned, although you don't want to go overboard with this; you should only share standard assets that doesn't change very often, and keep project-specific assets in their own repo.
EDIT: wanted to add, for React components, this is actually a really good practice, because it forces you to write generalized, independent components. You can use something like React Storybook to create these components in isolation. You can publish one master package with all the packages, individual components as packages, or somewhere in between (eg. all Form components in one package). Either way, though, they can still share a repo for convenience.

Reduce Size of Large, React based Single Page Application's Browserify Bundle File

I am building a reactjs application using gulp and Browserify. I have used material-ui components in my application. There are 8 pages in my application using different components.
The build.js created by Browserify is 4mb in size. I want to reduce its size. I have searched and learned a little about the lazy loading design pattern. But I am confused how it should be used to make the size smaller of my application?
I am also using react router in my application so there are 8 routes defined. I have an idea that we can lazy load the files required for each route, but how could that be done in react?
P.S : I would have loved to share some code, but i don't understand what kind of code will be required for such a question.
The problem is most likely that your javascript contains source maps. Source maps can easily increase the size of your code by an order of magnitude. Try taking a look at your compiled JS file and if you see large random strings that look something like this:
dlfheihfgrewifjwe;iofgrewfwejroifnekw.nfoeiquf0eqf;oiwehjfkl;qwejfio;qeo;f;qoihfi;qejhfkjqwehj
then you are including source maps in your compiled Js.
Check your browserify config to make sure that you are not compiling with source maps.

Resources