Memory when importing js objects in react - javascript

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.

Related

Export a class or an object instance difference

I am confused, or I can say, I have no clue how the exporting actually works.
I have a React app, and I have some protected Routes. On login, I create a Client object instance and pass it to child components through Context, and then consume it.
Recently I saw an approach where the example code exported an object instance from a file directly and just imported it in files they wanted to consume it.
/** My approach **/
export default Example
/** The Object Instance approach **/
export default new Example()
What is the lifecycle of the object instance? Are there any disadvantages with the second approach, because it seems way easier?
If you export the class, with
export default Example
then consumers of the module will be able to instantiate their own instances, and each instance will be able to have its own data. For example
// 1.js
import TheClass from './TheClass';
const tc1 = new TheClass();
tc1.foo = 'foo';
// 2.js
import TheClass from './TheClass';
const tc2 = new TheClass();
tc2.foo = 'bar';
Both modules can continue to use tc1 and tc2 completely independently, since they're separate instances.
But if the original module exports an instance rather than a class, then all consumers of the module are forced to use the same instance:
// 1.js
import theInstance from '...';
theInstance.foo = 'foo';
// 2.js
import theInstance from '...';
// might not be a good idea to do theInstance.foo = 'bar' here
// because that will affect 1.js as well
// and will affect any other modules that imported the instance
In short - exporting the class is more reusable than exporting the instance. Sometimes potential reusability is something a script-writer will consider important, and sometimes it isn't. (And sometimes, even if you don't consider it useful initially, you may encounter a situation later that forces you to reconsider.)
And sometimes you want to make sure that there's only one instance ever in a script, in which case
export default new Example()
is a way to accomplish it.

Avoid usage of JS Map with Immutable Map

How can I avoid the confusion of native Map with Immutable.Map?
I use immutable.js in a react project (ES6 or later, transpiled with babel).
So a lot of files start with an import like:
import { Map } from 'immutable';
Everything is fine until somebody adds the above import to a file that uses native JS Map, so that new Map() becomes an Immutable.Map.
I could consequently import the whole immutable library (import Immutable from 'immutable';) and reference it using Immutable.Map. However, this has possibly impact on the size of the resulting code (the compiler is likely not able to figure out that not the whole imported lib is used) and probably does not look nice.
Are there better solutions? Can I somehow reference native JS Map specifically?
You have two options here. I like the first, but YMMV.
Native JavaScript modules allow you to alias not just entire library imports, but individual named imports: import {Map as IMap} from 'immutable';. Now there's no conflict and your bundle size should be smaller. Added bonus: the intent of your code might become more clear to future maintainers who will now immediately know they're not dealing with normal native Maps.
The actual constructor is a property of the global context: just reference window.Map which won't conflict with the one you imported into the module namespace.
I'd recommend either importing Map in your higher order component and passing it down to your component as prop or using an import alias (as proposed in #Jared Smiths answer)
HigherOrderComponent
import { Map } from 'immutable';
class HigherOrderComponent extends React.Component {
render() {
return <myChildComponent map={Map} />;
}
}
myChildComponent
class HigherOrderComponent extends React.Component {
componentDidMount () {
this.myMap = new this.props.Map();
}
render() {
// ...
}
}

How to import as a new copy of object?

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

How to import a module in es6 that itself needs to invoke/initialize its function/class before being imported

I was wondering what is the best practice to import a module's function/class in another module that the module itself needs to invoke/initialize its own function/class before being imported into another module? I don't know if I could ask my question clearly enough! So let's put it in an example.
This is my module:
// myModule.js
class MyModule {
constructor() {
// do sth
}
}
let myModule = new MyModule();
And this is how I like to import it in another module:
import MyModule from './myModule';
This actually works fine! But as you can see, in the myModule.js file I didn't export default my MyModule class because that's not the only thing that is happening in the myModule.js file! I'm also initializing the class after defining it... (I know that even if I have set my class as export default the initializing would still work fine while the module is imported somewhere else...)
So, without setting anything as exported inside of our module, or with setting the class as the export default, everything works fine when the module has been imported somewhere else... So far so good! But I'm looking for a best practice if there's one!
So here are my questions regarding such cases:
Is it ok to import a module that doesn't have anything for export?
Shall we set the class as the export default, although we are doing some more works outside of the class in the module (The initializing job that is happening after defining the class)?
Or maybe is it good to do the initializing job in another function and then export both, the class and the function, and then invoke the function to do the initializing job in the imported module?
Thanks a lot everyone! I really appreciate any helps regarding this :)
How about offering to import the class or the instance of it? Like:
// export class itself
export class MyModule {
constructor() {
// do sth
}
}
// export instance of MyModule directly
export default new MyModule();
// export a factory function if you need more work to be done
// before the instance is created
export function myModuleFactory(...args) { // define e.g. arguments to be passed to constructor
// ... do stuff
const myModule = new MyModule(...args);
// ... do more stuff
return myModule;
}
So you could do:
// import instance
import instance from './myModule';
// or class
import { MyModule } from './myModule';
// or import factory
import { myModuleFactory } from './myModule';
What to do, depeneds on what you want to accomplish with your module. If you want your app use one shared instance of a MyModule class object, you’d export and import the instance like shown above. If you want to create multiple instances in different contexts, you’d export the class itself or a factory function to return a new instance.
To keep it even more clean, you’d keep the class in yet another separate file and import it into the module providing the factory/instantiation.
Update
To answer your first question: You can import modules that don’t have any export defined. The module will be loaded and its logic executed. The thing is that as long as it won’t change global variables (like window in web development) it won’t have any effect as everything inside the module happens within an isolated scope. As you may have guessed, having modules change global vars is far from best practice.
Thanks a lot guys for your answers. I really appreciate that. Actually I got really confused on the project that I'm working on, so maybe I couldn't express what I'm trying to do...
But anyway, I write my own answers to my questions, maybe someone else find them useful:
It's always a good practice to have a export default or export for a module that we're going to write. Because every piece of code tends to have some results, right? So in a module, we should consider what we're going to achieve at the end and then export that. Export your outputs, the things that you expect your module to provide when it is going to be imported somewhere else.
If your module is a single class, it's good to export default it. Otherwise, as I said in the first answer, it all depends in what you're going to achieve in your module and what is your results. Export all the results, utility functions, etc...
You may like to do that too! But first think about your use case. As soon as the module is imported somewhere else, all the codes inside of it will be executed. So do whatever you like to do and then export the end results.

How to properly use es6 classes in different files by importing them in Meteor?

I have recently discovered Meteor and I am struggling with using ES6 classes and imports in a new Meteor project. What I want to do is to have a complex structure of classes, which methods get called from Meteor events/methods/helpers. I've added Babel.js to the project by writing a command $ meteor add grigio:babel and it works properly.
Example of what I am trying to achieve:
in server/models/article.js:
class Article {
static all() {
//returns all articles from db
}
}
in server/methods/articles.js:
Meteor.methods({
allArticles: {
Article.all();
}
})
Having just that raises ReferenceError: Article is not defined in a methods file, which is adequate. So I have got three options: write all classes in one file, append all classes to a global object or use a good module system like Browserify. Obviously, third option is better.
But how do I use that? Babel converts export, import into Browserify by default and Meteor raises a require is not defined error on page refresh. After googling the problem I didn't find a clear solution on how to add Browserify to Meteor. Should I add a npm packages support to Meteor, add a npm package of browserify and add it manually to Meteor on every page where I import/export anything? Or should I use a completely different approach? How is this task usually handled in Meteor? Thank you!
I was reading about this earlier and found this issue on github that may help.
Essentially just assign the class to a variable that is exposed to both the client and server (lib/both/etc depends on your file structure). Like so:
Article = class Article {...}
Seems to be the best solution at the moment.
The way I do this is to collect objects together into various namespaces, for example:
// Global
Collections = {};
class Article {
static all() {
//returns all articles from db
}
}
_.extend(Collections, { Article });
Then to avoid having to use Collections.Article everywhere I can use the following in the file I need to access Article in:
// Make `Article` available
let { Article } = Collections;
I am using Meteor 1.4.1.1 and the error remains, when reproducing your approach. However, there are some new ways to use es6 classes now:
1. Export your class as a constant (e.g. for use as a singleton object):
class MyModuleInternalClassName {
//... class internals
}
export const PublicClassName = new MyModuleInternalClassName();
You can import this one via
import {PublicClassName} from 'path/to/PublicClassFileName.js';
2. Export your class directly as the module's default
export default class PublicClassName {
//... class internals
}
and then import it (as with the above one) as the following
import {PublicClassName} from from 'path/to/PublicClassFileName.js';
let myInstance = new PublicClassName();
+++++++++++++++++++++++++++++++++
Regarding the question of OP and the error, you can try something like this:
Article.js
class InternalArticle {
constructor(){
//setup class
}
all() {
//returns all articles from db
}
register(article){
//add article to db
}
}
export const Article = new InternalArticle();
Import and use the Singleton
import {Article} from 'path/to/Article.js';
//either register some article
Article.register(someArticle);
//or get all your articles
const allArticles = Article.all();

Categories