💡

How to pre-load data into the React.js Application

Background

We need to add an application to several backend management applications using the same backend API. The interface requirements among those applications are the same. So it would be easy to use a plugable solution to integrate UI to different pages. There are two options: <iframe> and React.js. We choose React.js because we built a platform using our in-house Server Side Rendering (SSR) framework based on React.js . The platform is now deprecated but the component code we can still reuse.
The platform was an SSR application, its i18n component loads the specific language text and render it to html and then return HTML back to the client. If we reuse these components we can once again reduce the time to develop the application.

Problem

We had a big repository containing all the component and text info we used to build a platform. If we compile all translation info into a single JS file, the SPA file will be too large for client to download. (More than 2MB)

Solution

So how to reduce the size of JS file compiled?
We can reduce the size of JS file by injecting the translations dynamically and only inject those used ones, with the power of webpack plugin.

Brief Intro of Webpack Loader

The basic flow of webpack compilation is as follows, the loader is called twice before and after compiling the file.
A standard plugin looks like this:
module.exports = function (content) { return someOperation(content); }; module.exports.pitch = function (remainingRequest, precedingRequest, data) { if (someCondition()) { return someContent; } };
notion image
This allows us to embed the settings with two steps:
  1. During pitch method, we read the file that contains used tags and store inside memory.
  1. Inside normal export, we replace the placeholder with multi-language informations in json format.

Implementation

The Loader:
// This is the function we use to load Translations and Mappings. const loadTranslationsAndKeyMappings = require("./loadi18n"); // This passes in the Options of webpack. const { getOptions } = require("loader-utils"); const path = require("path"); module.exports = function (source) { console.log("Trying to replace i18n information..."); const i18nAbsolutePath = path.join(__dirname, "../i18n"); console.log(`Reading i18n info from ${i18nAbsolutePath}`); const i18nObject = loadTranslationsAndKeyMappings(i18nAbsolutePath); const objectString = JSON.stringify(i18nObject); // Substitute the string with our data. return source.replace( `'DATA_PLACEHOLDER'`, "`" + objectString + "`" ); };
Config file of craco:
// craco.config.js module.exports = { webpack: { configure: { module: { rules: [ { test: /I18N_DATA\.js$/, loader: "i18n-loader", }, ], }, }, }, }

Conclusion

Through this way, we can easily control the size of the overall js bundle and succeeded in making it smaller than 200KB at last. As a result, when we want to load some static info into our webpack application or perform some custom handling on certain files, we can always consider using our custom loader, and it’s not as scary as it might sound.