React components large size - reactjs

Is it normal to have over 700kB in file size for a react component?
This is the file size after compilation.
GridComponent.js 732 kB 0 [emitted] GridComponent
main.js 7.97 kB 1 [emitted] main
GridComponent is the react component. main is normal js for comparison.
Is that normal?
I plan to only include react components in pages that requires them.

I can see that you're using webpack in your app, and your GridComponent.js should have something like require('react'); (or import React from 'react';, if using ES6 modules with babel).
When you require things, webpack will "join" all your dependencies in the specified "output" settings (provided in webpack.config.js). So, if you check the contents of GridComponent.js you'll see that React is inside. In your case, this GridComponent.js is your "bundled file" (it will contain all the dependencies that you've imported). That's why it seems to be "large", because this file contains your component and React itself (and other dependencies as well).
When you say "I plan to only include react components in pages that requires them.", I think you may want to split your code, so your page will only load the things you need. You can check more info about code splitting here:
https://webpack.github.io/docs/code-splitting.html
A basic "splitting" strategy would be to split your "vendor" code from your "app" code (https://webpack.github.io/docs/code-splitting.html#split-app-and-vendor-code). In your case, React, React DOM and other "common" dependencies would go to "vendor", and your app is the code you've written.
But just as tip, code splitting is only really needed when you have lots of code. Because sometimes an extra network request may introduce more "latency" than downloading a few more bytes. Or if you have some "special" caching strategy, since your app may change more times than your vendor code (your vendor code will only be downloaded again when some dependency version is changed), and you avoid downloading lots of bytes when a simple thing is changed in your app.
I think this solves your question, but to add more information here, if you want to distribute your code to more people (i.e. an open source project), you may distribute your code and say "ok, to run that, you must install that other dependency", and you can achieve that with peer dependencies.
If peer dependencies is not your case, you can save some bytes by minifying your file (you can do that by modifying your webpack configuration). A good blog post talking about that can be found here:
http://moduscreate.com/optimizing-react-es6-webpack-production-build/

Related

Does same imports in different files result in increased build size in ReactJS?

I have a personal project in which I am using react-bootstrap. So my question is if I import, let's say a Modal Component in two different react components of mine, will it result in increased build size?
If it does then what is the best way to import to prevent the unnecessary increase.
I tried experimenting with builds but didn't find any increase.
If we are specifically talking about bundle size i.e. the size of the initial bundle that's downloaded when a user accesses your page, then no that's not an issue (apart from a few bytes to store the reference).
This is because react-bootstrap (the parts that you use) will be bundled with your web application (depending on your webpack config), and then reused throughout you application.
As for what you can do to reduce this:
You can import the subfolder of the library that you need. e.g. instead of importing {Modal} from "react-bootstrap" you would import Modal from "react-bootstrap/modal", although I'm pretty sure create-react-app does that for free when you run yarn build (please double-check that part).
Here is a new React create app after running yarn build:
Here is the build size of the create react app with 1 modal:
Here is the build size of the create react app with a 2nd modal:
As you can see there is a clear difference in size from the 1st image to the 2nd image, but a very small difference between the 2nd and the 3rd.

What is the correct way to include React in both an application and a private library? (React Invalid Hook Call warning from duplicate React)

I have a sort of a "monorepo", one big project consisting of a few smaller projects that use React.
I'm trying to break these up into three separate repositories, lets call them Core, Application1, and Application2
The Core is a dependency of both applications, and the Core depends on React, because it defines some React component classes. The applications both also use React.
When I tried to build this all together (using Parcel bundler), I am getting a final bundle which at runtime gives the Invalid Hook Call warning in one (but not both) of the applications.
On that page (or in the error message), it says that the error could be caused by one o these:
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
I have checked that #1 is not true, and I'm not even using hooks in any way that I am aware of, so the problem is seems to be multiple versions of React.
I gathered from reading about this that it was a mistake for my Core library to declare React as a dependency, and that it should instead declare it in peerDependencies. That made the Application stop giving the error, but it also made my Core library start having a bunch of Typescript errors and failing to be able to run the unit tests (which rely on React, using Jest/Enzyme to render and validate DOM).
Since specifying React in peerDependencies caused it not to be installed in the node_modules of Core, I decided that I should probably include React in both the peerDependencies and the devDependencies of Core. That fixes the Core library again but breaks the Application.
I'm not really sure of the following:
Why one of my applications fail due to duplicate React copies and the other doesn't, since they seem pretty symmetrical to each other.
Why, even though I only specify React in peerDependencies and devDepenencies in Core I still would get a duplicate copy of React in the dependent application
Whether the method used to bring Core in to the application has any bearing on this. (one method I'm trying is package.json I specify core as a "file:../" style of URL. Another alternative is to use "yarn link", or possibly to do both of these, and I'm not sure whether this has any effect on what ends up in node_modules underneath the application folder or on what gets bundled)
What is the right way to include React in both an Application and a library, in such a way that both of those projects have React available but there does not end up being duplicates in the Application causing this hook error (or, just taking up extra space).
Answering my own question.
I found the following issue helpful: https://github.com/facebook/react/issues/14257
Various different suggestions were made in the comments of ways to solve this problem, either by npm link or yarn linking the react library from the library to the application, or vice versa. Those all seemed promising, since the idea is to make sure that all of the different references to React are actually pointing to the same place. Unfortunately none of those worked for me. (e.g. the answers by JerryGreen and Kas in that issue)
Another user, dcecile, suggested using webpack's alias feature, but I'm not using webpack.
resolve: {
alias: { react: require.resolve("react") }
},
Parcel has a similar alias feature but can't be used in quite the same way because it's used in the package.json file so things like require.resolve can't be called like they are in webpack's js config file.
I ended up finding a way to use Parcel's alias feature to do what I wanted, based on another example from https://github.com/jaredpalmer/tsdx/issues/64 from user jaredpalmer. In my situation, I'm adding this to the application's package.json, and it appears to get rid of the duplication problem and the "Invalid Hook Call" error:
"alias": {
"react": "../my-core-library/node_modules/react",
},

React root.js weights about 9MB

Ok, so... I have this project where we are using a lot of components (as usual in every React project) but when we compile the project to see it in the browser (ie. foreman start) it takes a lot of time to load and also when it loads it creates a file that is very very large.
We don't know why this is happening, probably something that we messed up and we don't know yet.
I don't know which part of code you need to see to help me understand why this is happening. Let me know and I will give it to you.
Also, I wanna know if there is a way to prevent that file to be that large, as long as I know it should weight no more than 2Mb or something...
Thanks in advance!
The typical reason for such issues is not the amount of components you have made but rather the libraries you use. You can check what is the cause for a large bundle by first running
webpack --json > stats.json
and then installing and running webpack bundle analyzer
webpack-bundle-analyzer stats.json
After this you need to start working on fixing the cause of the large size.
Normally that would depend on what the reason for large bundle size is..but the following methods will definitely help.
code splitting for react
code splitting for webpack
Using webpack's optimizations
And, since sometimes tree-shaking doesn't work as well as it is expected to..you could change your imports from the style of
import {x, y} from 'library';
to
import x from 'library/x';
import y from 'library/y';
Which would ensure that if there are 10 exports from the library, then all of them dont get loaded... Only those that are needed do. For example, if you are using lodash,
import {debounce} from 'lodash';
will import the entire lodash library while
import debounce from 'lodash/debounce';
will only import that particular file.
There are other ways to optimize your output (like webpack externals) but that is dependent on what is actually causing the issue.
NOTE A lot of this answer assumes you are using webpack to bundle code. However if that is not the case you will need to search out optimization and bundle analyzed for the specific bundling tool you are using

React Loadable SSR isn't always giving client complete list of bundles, leading to page flash

I've been struggling for the last few days to eliminate page flash. My project started with create-react-app, then I used help to set up SSR and various other options. When I started running into problems, I stripped my code down and ejected. Here is my code. You can build with npm run build and start the SSR with npm run serve. Visit http://localhost:3000/ to see the result.
There are two routes in my app: Home and About.
Going to / (Home) works as desired: react-loadable identifies the home bundle, injects a <script> tag for it, and sends the page out the door. The client loads all the bundles and react takes over with no flash.
Going to /about results in a page flash (make sure the page is loaded from the server to see this). I added an import for moment-timezone into the about component, and webpack smartly moves it into its own bundle, separate from the about bundle. The problem is that react-loadable only sees that it needs the about bundle, not the extra bundle containing moment-timezone. Thus, on the client side, when react takes over it sees that its missing a module, blanks the page, loads the bundle containing the missing module, then re-renders (at least,
I think that's how it works).
What am I missing? I am using the babel and webpack plugins as described in the React-Loadable Readme. It seems like React-Loadable is only smart enough to go one level deep and not see what the imported module's dependencies are, but surely that's not the case, is it?
This happens because react-loadable don't have a nice and deep implementation for server side rendering.
But the good news is, there's an add-on for that, it is called react-loadable-ssr-addon.
In the plugin repository it says:
React Loadable SSR Add-on is a server side render add-on for React Loadable that helps you to load dynamically all files dependencies, e.g. splitted chunks, css, etc.
Oh yeah, and we also provide support for SRI (Subresource Integrity).
So basically this plugin handle everything regarding the assets situation for server side rendering in react, reproducing the same behaviour as the tradicional approach (client side render).
With that, your assets are managed and loaded automatically to your output, avoiding the error you reported.

Redux + React understanding size of webpack bundle

I read various answers like this regarding reducing the bundle size. First I wanted to understand what is going on with my 13MB bundle, so I've installed the Webpack Bundle Size Analyzer. It generated this output:
There are a few things I don't understand:
Why is react appearing twice with two different sizes?
How come it doesn't add up to 13MB? (it's around 4.75MB)
Does this means my own code is only 4KB? (last line)
Is it possible to understand from this tree how to save KBs? I mean, for instance, is there a way to use only parts of lodash or is it required as a whole by React?
Update:
Found the answer for #1. React-color requires ReactCSS which requires a different version of react and lodash. Without React-color my bundle dropped from 13MB to 8MB. I will try to find a way to re-use react and lodash.
It seems that you have eval-source-map devtool option in your webpack config file, that's why you are getting such huge bundle size. This way, sourcemaps are putting inside of bundle js file.
Try to change this option to source-map to build source maps separately.
// webpack.config.js
{
devtool: 'source-map',
// ... other options
}
1) Sometimes other dependencies require again lodash and / or react, so these dependencies are pulled in multiple times by webpack (usually happens when the version differs), here the DedupePlugin should help. The final bundle size also depends on your devtool entry in webpack, so in development it is usually much bigger as you probably have sourcemaps enabled and a your build is not optimized. Hard to tell without your actual webpack config.
2) Maybe webpack is already deduping?
3) last line says 400KB
4) You can definitely save a few kbs. A good way to start is using lodash-webpack-plugin with babel-plugin-lodash, this will import only the lodash methods you are actually using instead of the complete library. You can also just require the bits you need from material-ui as outlined in this answer.
I recently added the webpack-dashboard to my webpack config, so I instantly see the output and percentages that make up my bundle size, so maybe you think twice about adding huge dependencies if you don't really need them.

Resources