Dynamic export of variables ES5 to ES6 - javascript

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

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.

Is there a way to lazy export file with ESModule?

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;
}

How to wrap every exported comopnent with HOC?

I need to add to ALL of my React function components the possibility to add [data-test-id] attribute for testing purposes. To achieve that I created withTestId() HOC which adds optional prop testId to wrapped component and when it's defined it adds [data-test-id] to final HTML.
So when I define component like:
<ExampleComponent testId="example" />
it returns:
<div data-test-id="example" />
The only problem I have is to apply it to every component without the necessity to wrap it individually in every component. So instead of writing code like:
function ExampleComponent() { ... }
export default withTestId(ExampleComponent)
I would like to wrap all of my exports in my index.ts file, which right now looks like this:
export { default as ExampleComponent } from "./ExampleComponent";
export { default as ExampleComponent2 } from "./ExampleComponent2";
...
How can I achieve this?
I see two ways of doing this; One dynamic way, making the user-code of your library a bit more convoluted. with you being able to change the implementation easily and another one with a bit more boilerplate code, keeping the user-code as it is.
I haven't tested their behavior regarding tree-shaking when bundling the code.
Using destructing in user-code
This allows to add / remove things from your main component export file without having to worry about additional boilerplate in your library. The higher-order-component can be switched on/off easily. One caveat: The user code needs to use destructuring to retrieve the components.
Your new index.ts file would look like this, while I've called your previous index.ts file components.ts in the same directory:
import * as RegularComponents from "./components";
import withTestId from "./with-test-id";
const WithTestIdComponents = Object
.keys(RegularComponents)
.reduce((testIdComps, key) => {
return {
...testIdComps,
[key]: withTestId(RegularComponents[key])
};
}, {});
export default WithTestIdComponents;
To use it in your application code:
import MyComponents from "./components/tested";
const { Component1, Component2, Component3, Component4 } = MyComponents;
This uses the default export to make it look like you have all components in one place, but since you cannot destructure exports directly, you need this second step to get the correct components out of it.
Add boilerplate to the export file
Since there is an index.ts file with all the components exported in the library, one could import/rename each component and re-export them with withTestId and their name:
import withTestId from "./with-test-id";
import { default as TmpComponent1 } from "./component1";
import { default as TmpComponent2 } from "./component2";
import { default as TmpComponent3 } from "./component3";
import { default as TmpComponent4 } from "./component4";
export const Component1 = withTestId(TmpComponent1);
export const Component2 = withTestId(TmpComponent2);
export const Component3 = withTestId(TmpComponent3);
export const Component4 = withTestId(TmpComponent4);
This way, imports can be used as before:
import {
Component1,
Component2,
Component3,
Component4
} from "./components";
I'd argue that using index files already is some kind of boilerplate and this approach adds to it. Since the user code does not need any changes, I'd favor this approach.
In one of our projects, we have used a custom takeoff script to create this kind of boilerplate for us, whenever we generate a new component.
Examples
Here is a code sandbox to see both approaches.

Export React Components dynamically

I need to add some extra properties/functionality to my components using HOC and then export them.
I have a HOCWrapper component that is a HOC, that is implemented like mentioned here.
index.js
import * as all from './AllMyComponents'
let AllChangedComponents = Object.keys(all).map((key) => {
return all[key] = HOCWrapper(all[key]);
})
export default AllChangedComponents;
And the use the like:
another.js file
import {MyComponent} from './index'
This is not working when trying to export dynamically the components,
Its is possible to export dynamically?
You won't be able to do import {MyComponent}; that only works if MyComponent was statically exported from index.js.
Since MyComponent is instead a member of an object that is being exported from index.js, you need to import that object and destructure it into local variables:
import wrapped from "./index";
const { MyComponent } = wrapped;
Another issue here is that your wrapping code doesn't make any sense. Instead of computing an object with wrapped functions, you're trying to mutate the original import (which is only visible in the module's own scope, and doesn't affect anyone else who imports ./AllMyComponents).
You should do:
const AllChangedComponents = Object.keys(all)
.map(k => ({ [k]: HOCWrapper(all[k]) }))
.reduce((l, r) => ({ ...l, ...r }))

ES6 Imports inside Export default

I'm currently migrating the whole code of a NodeJS application from ES5 to ES6/7.
I'm having trouble when it comes to imports :
First, I understood that making an import directly call the file. For example :
import moduleTest from './moduleTest';
This code will go into moduleTest.js and execute it.
So, the real question is about this code :
import mongoose from 'mongoose';
import autopopulate from 'mongoose-autopopulate';
import dp from 'mongoose-deep-populate';
import { someUtils } from '../utils';
const types = mongoose.Schema.Types;
const deepPopulate = dp(mongoose);
export default () => {
// DOES SOMETHING USING types AND deepPopulate
return someThing;
};
export const anotherModule = () => {
// ALSO USE types and deepPopulate
};
Is this a good practice to have types and deepPopulate declared outside of the two exports ? Or should I declare them in each export ?
The reason of this question is that I'm having a conflict due to this practice (to simplify, let's say that dp(mongoose) will call something that is not declared yet)
You can only have one 'default' export to a module, or you can have multiple 'named' exports per module. Take a look at the following for a good description of handling exports in ES6: ECMAScript 6 Modules: The Final Syntax

Categories

Resources