In my react application, I want to use a library (money.js) in 2 components which have different settings.
This library:
http://openexchangerates.github.io/money.js/
http://openexchangerates.github.io/money.js/money.js
I checked that javascript is using reference so the 2 components are actually referencing the same thing.
Does import create a new copy of imported library?
ES6 variable import by reference or copy
Is that possible without changing the source code (ideally), I can do something like the following
// a.js
import fx from 'money'
fx.rates={USD:1, EUR: 2.001}
// b.js
import fx from 'money'
fx.rates={USD:1, EUR: 2.002}
In my situation, I noticed that changing the rates in b.js, it also affect a.js.
No, there's no general one-size-fits-all way to clone a module or load separate copies of it. You may be able to do it, but it will be dependent on the specific way the module is coded.
It's unfortunate that the library you're using doesn't have the concept of an instance rather than making everything global to the library. You might consider adding that concept to the library and sending the original repo a pull request (if they decline it, you can always create a fork).
You can do it simply by adding query-param to module filepath:
// a.js
import fx from 'money?first'
fx.rates={USD:1, EUR: 2.001}
// b.js
import fx from 'money?second'
fx.rates={USD:1, EUR: 2.002}
you can clone that rates object in each file :-
var localRatesVariable = Object.assign({}, fx.rates);
Modules are evaluated once, that's one of their main properties. It's possible to re-evaluate a module in some environment that supports it (Node.js) but not in client-side application.
In this case the library is hard-coded to use fx.rates object. Even if it's copied, it will continue to use it.
A proper way is to modify library source code to support multiple instances.
An alternative is to create a wrapper that hacks the library to behave like expected. Considering that it's convert method that uses fx.rates and it's synchronous, it can be patched to swap rates property during a call:
import fx from 'money'
export default function fxFactory(rates) {
return Object.assign({}, fx, {
convert(...args) {
let convertResult;
const ratesOriginal = fx.rates;
try {
fx.rates = rates;
convertResult = fx.convert(...args);
} finally {
fx.rates = ratesOriginal;
}
return convertResult;
}
});
}
// a.js
import fxFactory from './fx'
const rates={USD:1, EUR: 2.001};
const fx1 = fxFactory(rates);
You can deep copy object:
let localRatesVariable = JSON.parse(JSON.stringify(fx.rates));
Related
I am new to react and its transpiled way of generating javascript.
In react side, I have a class Utility that uses a data object UserData organized as below -
UserDataObj.js
class UserData{
this.someobj = {};
//some function here
something(){
}
}
const UserDataObj = new UserData();
export {UserDataObj};
Utility.js
import {UserDataObj} from './data/UserDataObj';
class Utility {
doSomething(){
//UserDataObj.something();
}
}
const utility = new Utility();
export {utility};
I have another ReactApp UserApp.js, that also uses UserDataObj and Utility (although not good design wise) -
import {UserDataObj} from './data/UserDataObj';
import {utility} from './Utility';
class UserApp extends React.Component{
//does something with UserDataObj
// also does somethign with utility
}
My question is, how many utility and UserDataObj instances will be created in memory, when UserApp is rendered. My guess is, it should be only 1 instance for both. But I want to confirm if importing n times creates a new instance every time.
Any good read on this topic is greatly appreciated.
Thanks
This depends on the bundling tool, and not React. I imagine that the new browser ES Module resolution scheme works in the same way.
Most bundlers that I know of, and other import schemes such as Node.js' require module resolution will cache the import between files and always return the same exported objetcs. This is a requirement for prototype inheritance, for example, otherwise, it would mess up the instanceof operator.
That exported new Utility() instance will be the same for any module that imports it. In order to generate new instances, you would have to have a function.
My simplified server code looks like below.
server.ts
import google from "googleapis";
const androidPublisher = google.androidpublisher("v3");
app.use('something', function(req, res, n){
...
})
...(only one of the dozens of other methods use androidPublisher)
I am importing googleapis library in order to setup androidpublisher variable. However, this googleapis library is big and it takes 400ms~700ms to fully import file, when it takes 10ms~30ms to import other library files.
Because my environment is serverless architecture (firebase functions), AND because approximately 1 out of 100 requests actually need androidPublisher, I want to take advantage of dynamic import to import googleapis when it is necessary. Otherwise, above setup actually adds 400ms/700ms latency to every request that spins up new serverless instance, even when androidPublisher is not needed.
So I have made changes like below.
server.ts
let androidPublisherInstance:any;
async function getAndroidPublisher() {
const googleapis = await import("googleapis");
if (androidPublisherInstance === undefined) {
const ap = googleapis.google.androidpublisher("v3");
androidPublisherInstance = ap;
}
return androidPublisherInstance;
}
...(one of methods use getAndroidPublisher() to get androidPublisher instance)
with above setup where I am using global variable & helper function to initialize androidPublisher only when needed. This works as intended and 400ms~700ms latency gets added when androidPublisher is needed for the first time. However, I ended up with type of androidPublisherInstance to be any. I couldn't correctly define the type because type definition is available inside of googleapis and that is residing inside of getAndroidPublisher function.
Thus, I lose all benefit of using typescript and have to play guess games while using methods/properties when I use androidPublisherInstance.
And I think I must use global variable, because I do not want to initialize androidPublisher multiple times (googleapis.google.androidpublisher("v3")) whenever a function calls getAndroidPublisher()
Am I missing something? Is there a way to use dynamic import & let client to be initialized only once without needing to use global variable?
You can just import the type. As long as you use it only in type definitions, not in value expressions, the compiled JavaScript will never load the module:
import { androidpublisher_v3 } from "googleapis";
let androidpublisher: androidpublisher_v3 | undefined;
Alternatively, to make sure you don't accidentally reference it in the wrong place, use only import types:
let androidpublisher: import("googleapis").androidpublisher_v3 | undefined;
I'm trying to create a build pipeline which doesn't bundle the files together, but instead uses <script type="module">. This will let me just recompile files as they change without rebundling, greatly improving build times during development.
Our project uses ES6, so this is generally easy.
There is however one snag: third-party modules that only have CommonJS builds (such as react).
There are a few ways around this. For now, I have a transform that changes the import name from react to /node_modules/react and my server is smart enough to then go find the appropriate dist file from node_modules and serve it up. This all works fine.
The problem is that it gets confused when I try to do something like:
import { Component } from 'react';
That won't work how it currently is (because it gets confused by there not being a default). However, this will work:
import * as React from 'react';
const { Component } = React;
I could manually do this for all files and packages, but a) that would make it unnecessarily ugly (with Redux and other things, there are half a dozen different packages in many files we'd have to do this to and b) there are lots of files, I don't want to manually change them all.
Is there a Babel transform plugin that can automatically make this kind of conversion? It seems that this isn't a completely novel approach, so I'm hoping there is a plugin that'll do it for me that my Google-fu failed to find.
I managed to get this working. I ended up having to write my own Babel plugin which makes the changes that I was looking for. Basically, for the different versions of the import statement, it changes it around in order to work better with UMD and CJS modules.
For example, something like this:
import A, { a, b, c } from 'a';
was transformed into something like this:
import * from __babel_A from 'a';
const A = __babel_A.default && (__babel_A.default.default || __babel_A.default) || __babel_A;
const { a, b, c } = __babel_A.default || __babel_A;
This format came about based on the way most things export. Many (like React) would put everything into an object with it's name on either module.exports (if I provided a fake one) or this or window. Others ended up not grouping things together (like ReactRTE) and had its own "default", which is where the default.default bit came from.
In addition to this transform, when my server serves up the dist versions of the third-party files, it'd wrap them up in a way that would then let me do an export default. That looked like this:
const module = {};
const keys = Object.keys(this || window);
const toExport = (function __auto_wrapper() {
${fileCode}
return module.exports || Object.keys(this).reduce((r, k) => keys.includes(k) ? r : Object.assign(r, { [k]: this[k].default || this[k] }), {});
}).call(this || window);
export default Object.keys(toExport).length === 1 ? Object.entries(toExport)[0][1] : toExport;
Again, the different ways are based on what the different projects output. Some will use module.exports when given it, others won't consider module.exports a real thing since I (purposely) didn't initialize export as an object (if I did, it'd try to use require() which I didn't want). In my case, ReactRTE had just a CJS module instead of an UMD, so I also had to do replace on its code to replace require('react') and require('react-dom') with references to the objects on window instead.
This all works as I wanted (completely unbundled code loading in the browser). The only slight side-effect is that React and friends are all available on window (which they normally wouldn't be if bundled properly), but that's pretty minor.
I'm trying to convert an old JavaScript library into ES6 compatible module.
The library is tracking.js (https://github.com/eduardolundgren/tracking.js/blob/master/build/tracking.js) but all my results ends with: Cannot read property 'xxx' of undefined
Is there any easy way to use such module? I'm trying to create just basic example like https://trackingjs.com/docs.html#step-2
Update
Because there is a request for more code. Let me show one of the non-working examples (part of Vue.js component):
import tracking from 'tracking';
export default {
created() {
const colors = new tracking.ColorTracker(['magenta', 'cyan', 'yellow']);
}
};
And the error is TypeError: _tracking2.default.ColorTracker is not a constructor
You should use the exports-loader, no modification of the library is necessary, the loader will look for the variable on the global scope, e.g:
import * as tracking from 'exports-loader?tracking!tracking';
The exports-loader needs to know how to access the module on the global scope (tracking.js assigns its self to window.tracking). Here you're telling it to use the exports-loader with parameter tracking (after the query question mark) to load the module tracking (after the explanation mark).
I'm currently importing a few lodash functions with the following syntax:
import {cloneDeep as _cloneDeep, each as _each, size as _size} from 'lodash'
This works, but it's kind of annoying to have to specify each as variable renaming. Are there any shortcuts that would avoid this? I'd be happy to have all those methods inside an object as well, to simulate the functionality of when the entire lodash library is imported, e.g. _.cloneDeep, _.each, etc.
To clarify, I don't want to import the entire library. So I'm looking for a solution that still allows the modular importing of functions.
Since your goal is to avoid annoyance I suspect this may not be what you want but if you're very set on the consumer syntax and want individual importing for efficient packing or something you could always create an intermediary module.
This module will import the contents that you want and in your main code you can reference the intermediary module in the way you're hoping to.
As an example:
import {parse as _parse, join as _join} from 'path'
export var parse = _parse;
export var join = _join;
and in your main application,
import * as path from './custom-lib/path'
path.parse(...);
It involves an extra file which sucks, but you do only have to write it once.
I wouldn't necessarily call this a good solution, but I think this is the closest you're going to get to what you want with the current standards.
ECMA2015 has the option to import all into one object/container.
import * as _ from 'lodash'
then you can use it as before:
_.cloneDeep(), _.each(), etc...
You can import the whole lib as well as individual methods:
import _, { cloneDeep, each, isArray } from 'lodash';
_.isArray([]) // true
isArray([]) // true
When you do import x from 'y'; it will store the whole y module in the x variable.
Doing import {a, b, c} from 'y'; will just pull in the specific methods a, b, c of the module.
You can combine both types of import by just delimiting them with a comma as I did in the example.
Learn more at MDN's reference.