Is there a way to lazy export file with ESModule? - javascript

I have a project which has many files to export. For now I use CommonJS to lazy export those files:
module.exports = {
get AccessibilityInfo() {
return require('../Components/AccessibilityInfo/AccessibilityInfo');
},
get ActivityIndicator() {
return require('../Components/ActivityIndicator/ActivityIndicator');
},
// .... many other files
}
ReactNative do the same thing React Native, so that a file is only loaded when it is imported specifically.
I want to refactor this file with ESModule, but I can't find a way to export files lazily.
Is there a way to export files lazily with ESModule?
Is it necessary to export files lazily with ESModule?

The ECMAScript way of doing this is via dynamic import(). The syntax is basically the same and it does what you'd expect, except that it returns a promise (which is great - it means that the operation does not lock the thread). Your code could e.g. look like this:
export const getAccessibilityInfo = () =>
import("../Components/AccessibilityInfo/AccessibilityInfo");
export const getActivityIndicator = () =>
import("../Components/ActivityIndicator/ActivityIndicator");
You would then grab these modules like this:
import { getActivityIndicator } from "./the/module/above";
const ActivityIndicatorPromise = getActivityIndicator();
// Whenever you need to use the ActivityIdicator module, you first need to await for the promise resolution
ActivityIndicatorPromise.then(ActivityIndicatorModule => {
// Do what you want here...
});
You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports. It also lists the cases where this syntax would be preferable. If you were hoping that this was possible using the static import syntax (import X from '../whatever';), rest assured - it is not.

No, there is not way to lazy export in react native
export does not have an effect on performance
import/require files have to make part of JS Bundle
components which are alive in memory put an effect on performance
In my point, there are three-way to make good performace
Solution 1: Dynamic Import
but you can load only dynamically component like this
const allPaths = {
path1: require('file path1').default,
path2: require('file path2').default
};
const MyComponent = allPaths('path1')
allPaths('path1') in this case only path1 component will be active
_
Solution 2: Stop render further Tree of Component
stop render on specific condition so further tree should not be active in memory and give not any effect on performace
render() {
const {isReady} = this.state;
if(!isReady)
{
return null;
}
return (
<ActualComponent />
)
}
-
Solution 3:Stop extra rerendering
you can use shouldComponentUpdate to stop extra renrendring. component on specifc condition only
shouldComponentUpdate(nextProps, nextState) {
return this.state.name != nextState.name;
}

Related

Importing / exporting Javascript Object Properties

I support a relatively complex legacy codebase, but am looking to modernise it a little by bringing in Webpack so that we'd have import & export capabilities in JS.
The problem I'm having is that we use a global object called App where we define and add different properties depending on the page. So for example we have the following file where we instantiate App (loaded on all pages):
app.js
const App = (() => {
const obj = {
Lib: {},
Util: {},
// etc
}
return obj;
})();
Then in another file we add to App.Lib just for the specific page that needs it:
lazyload.js
App.Lib.Lazyload = (() => {
// lazyload logic
})();
We simply concatenate the files during the bundling process, but obviously this is not ideal as none of the files have no knowledge of what goes on outside of it.
Exporting only seems to work for the top level object (where the object is defined), so anything I add to it elsewhere cannot be exported again. For example if I add export default App.Lib.Lazyload; at the end of lazyload.js and then try to import it elsewhere it will not import the Lazyload property.
Is there any way to get this to work without major refactor? If not, would you have any suggestions about the best way to handle it?
I don't think you can import Object.properties in JS. If you want to bundle specific packages (say Lazyload) for packages that need them, you might try:
//lazyload.js
export const LazyLoad = {
//lazyload logic
}
then somewhere else...
import {LazyLoad} from 'path/to/lazyload.js';
// assuming App has already been created/instantiated
App.Lib.Lazyload = LazyLoad;
Using Export Default...
//lazyload.js
const LazyLoad = {};
export default LazyLoad;
then...
import LazyLoad from 'path/to/lazyload.js';
App.Lib.LazyLoad = LazyLoad;
You can find help with Imports and Exports at MDN.

How to use React.lazy for a component of a module?

I am using the npm package called '#toast-ui/react-editor'. It includes a 'Viewer' react component. I could just use:
const Viewer = require("#toast-ui/react-editor").Viewer
But it increases the bundle size a lot. So I wanted to load it lazily whenever it is needed by using React.lazy. I am going to use it inside component:
<Viewer {...props} />
But I don't have any clue how to do it.
I tried this way, but didn't work.
const Lazy = React.lazy(() => import(require("#toast-ui/react-editor"))).Viewer;
I really want to know how to do it.
As Viewer is not a default component, it's not as simple as removing require, (which is not even needed, though).
You need to import it dynamically and then return the module as a default one (as that's what lazy expects and works with only).
const Viewer = lazy(() =>
import("#toast-ui/react-editor").then(module => {
return { default: module.Viewer };
})
);

Webpack-bundled code crashes when both `import` and `require` instructions are present in the ES6 code

Our Webpack bundle contains placeholders for the dynamic import of icons. An example dynamic import is as follows:
const { icon: iconName } = this.props;
const faIconName = `fa${iconName.replace(/./, c => c.toUpperCase())}`;
import(
/* webpackInclude: /\.js$/ */
`#fortawesome/pro-light-svg-icons/${faIconName}`
).then(faIcon => {
if (this.mounted) {
this.setState({ faIcon });
}
});
We decided for this strategy in order to prevent Webpack from bundling up the whole collection of FontAwesome icons.
Most recently we've realised the need to have internal builds where the dynamic import does not occur and pay the price of the larger bundle. The following code has been placed in our icon code (alongside the dynamic import above):
const faIconName = `fa${iconName.replace(/./, c => c.toUpperCase())}`;
let faIcon;
try {
faIcon = require(`#fortawesome/pro-light-svg-icons/${faIconName}.js`)[faIconName];
} catch (e) {}
Both loading strategies work fine when used one at a time. The issue comes when they coexist in the icon component.
I have verified that the import instruction leads Webpack to create in the bundle what to me seems a synthetic JS file generated with the command:
webpack:/// ./node_modules/#fortawesome/pro-light-svg-icons lazy ^\.\/.*$ include: \.js$ namespace object
When both import and require instructions are present in the Icon component, the synthetic file is different from when the sole import is encountered.
In detail, the object called map in the synthetic file contains values that are arrays with 3 elements in the import case and with 2 elements in the import+require case; the synthetic code tries to access the third element and everything crashes.
Can anyone comment on this issue?
I found an answer, you may check my full answer here
To make long story short i imported basing on the list and put all the imported components to Component's state. Afterwards i've created the React.createElememt's from the stored imported components and pulled all of them to Render
componentDidMount = () => {
//we get elements list from any source to redux-store
this.props.getForms();
//access redux-store to the list
const forms = this.props.configBody.sets;
//make deep object copy
const updatedState = { ...this.state };
updatedState.modules = [];
if (forms) {
//here is the very dynamic import magic: we map the import list and prepare to store the imports in Component`s state
const importPromises = forms.map(p =>
import(`../TemplateOrders/Template${p.order}`)
.then(module => {
updatedState.modules.push(module.default)
})
.catch(errorHandler(p))
)
//wait till all imports are getting resolved
Promise.all(importPromises)
.then(res =>
//then run setState
this.setState({ ...updatedState }, () => {
console.log(this.state);
}))
}
}

Dynamic export of variables ES5 to ES6

I'm working on a vue/nuxt.js project and would like to apply the atomic design methodology, i would like to import the components in a clustered and smarter way.
currently
import ButtonStyled from '#/components/atoms/ButtonStyled.vue'
import TextLead from '#/components/atoms/TextLead.vue'
import InputSearch from '#/components/atoms/InputSearch.vue'
How I wish
import {
ButtonStyled,
TextLead,
InputSearch
} from '#/components/atoms'
Solution?
index.js in folder of atoms
it works perfectly (ES5)
// ES5 works 👍
const req = require.context('.', false, /\.vue$/)
req.keys().forEach(fileName => {
const componentName = fileName.replace(/^.+\/([^/]+)\.vue/, '$1')
module.exports[componentName] = req(fileName).default
})
// ES6 does not work 👎
// ERROR: Module build failed, 'import' and 'export' may only appear at the top level
const req = require.context('.', false, /\.vue$/)
req.keys().forEach(fileName => {
const componentName = fileName.replace(/^.+\/([^/]+)\.vue/, '$1')
export const [componentName] = req(fileName).default
})
nuxt use ES6
NOTE: I can not export an object because I can not use import {ButtonStyled} or I will have to de-structure the object after importing it
I need to export so that I can use
import { ButtonStyled } from '#/components/atoms'
I need to export name of each component in the folder
Any advice, information or suggestions will be appreciated.
Thanks in advance.
Well first of all you need to be careful when making use of import/export on EM6, since now you can't export anywhere outside of the top level scope of the js file and the general treatment of it is different than in EM5.
Now with the problem. I see you are exporting the components from inside of a ForEach loop/function and that works totally fine in EM5 but with EM6 It's different, and at least I see two ways you can solve the problem if you aren't expecting the number of components to grow dinamically:
Call a function that returns the component and export it, do it for each component. Should look something like this:
./componentsFile.js
exports.component1 = () => { /*code...*/ return component }
exports.component2 = () => { /*code...*/ return component }
./renderingFile.js
import { component1, component2 } from './componentsFile.js'
/* use the components that you get from the return... */
The other way is to build an object which fields are the components. And destructure it when you are importing.
./componentsFile.js
const component1 = /*Create the component*/
const component2 = /*Create the component*/
exports.object = {
component1,
component2,}
./renderingFile.js
import { component1, component2 } from './componentsFile.js'
/*Use the components...*/
I think you can get the idea with this two ways.
I created a library that solved this problem for me, makes exports named from a directory and listens to the creation, rename and exlclusion of the modules and updates the index.js that does the export.
Maybe it helps other people.
named-exports

Only dynamically import module once rather than on every component instance in React

I have a react component that conditionally imports a module. The reason is if the module is imported normally at the top of the file, this breaks another project's webpack build that depends on this component because it cannot process the imported module.
I have solved this issue with es6 dynamic imports - however, the problem now is that every instance of the component re-imports the module. If I have 100 components on 1 page, it'll import that module 100 times, making it horribly inefficient and slowing page load times.
What would be the proper way to only import it once, then have the rest of all component instances reference that 1 dynamically imported module?
Here's my component:
import React from "react"
export default class Link extends React.Component {
state = {
gatsbyLink: null
}
...
componentDidMount() {
if (GLOBAL_FLAG) {
import("gatsby").then(({ Link }) => {
this.setState({
gatsbyLink: Link
})
})
}
}
render() {
const { gatsbyLink } = this.state;
const GatsbyLink = gatsbyLink ? gatsbyLink : "";
...
return (<GatsbyLink {...}>...</GatsbyLink>)
}
}
I'm not sure dynamically imported modules are cached. If they aren't you could export a promise that resolves a dynamically imported gatsby if the flag is set.
You will still have to import and call it in every component where you use gatsby, but it will not dynamically import the module every time.
const conditionallyResolveGatsby = () => {
// You could reject as well
return GLOBAL_FLAG ? import('gatsby') : Promise.resolve();
};
export default conditionallyResolveGatsby();
You could also try to use require.

Categories

Resources