I'm working on a simple React library but I'm unsure whether multiple objects are being created unnecessarily.
I have an app.js file:
class App {
method1() {
}
method2() {
}
}
export default new App();
I also have an index.js file:
import app from './app.js';
...
export default app;
In the index.js of my React project (where I make use of the library) I use:
import MyLibrary from 'react-library';
...
MyLibrary.method1();
and then I do the same in some of my components too:
import MyLibrary from 'react-library';
...
MyLibrary.method2();
Is the second import of MyLibrary a different object to the first MyLibrary?
Is the second import of MyLibrary a different object to the first
MyLibrary?
In general the object returned by the import is cached (same behaviour as nodejs require), multiples import of the same file will result in the same object being returned. So the answer to your question is No, you're dealing with the same reference in memory.
https://webpack.github.io/docs/resolving.html
Every filesystem access is cached so that multiple parallel or serial
requests to the same resource are merged
in your particular case, as suggested in the comments section, you're exporting an instance, not the class itself.
export default new App();
consequently each component that import that file will deal with the same instance.
This is a singleton pattern, don't know if it is the desired behaviour, if you want that each component has it's own instance you should export the class instead.
export default App;
You are creating a singgleton there, put a breakpoint in the constructor and you will see how only one instance is created.
Related
For example, the recommended way of importing in React Bootstrap is to go this way:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is "Doing so pulls in only the specific components that you use, which can significantly reduce the amount of code you end up sending to the client."
source: https://react-bootstrap.github.io/getting-started/introduction/
Same for React MUI components:
import Button from '#mui/material/Button';
source: https://mui.com/material-ui/getting-started/usage/
I want to implement something similar in my React components library, to limit the usage of code in the bundle, but I don't know how they implement this specific pattern. I have looked at their code base, but I don't quite understand.
Basically it is all about modules and module files and their organization. You can have a lot of.. lets call them folders, "compoments/*" for example. "components/button", "components/alert", "component/badge", and other things. All of them will have some index.js or .ts file that will export or declare and export all the functionality that needed in order to make this component work, 'react-bootstrap/Button' for example. Ideally all those subfolders or submodules are independend from each other, no references between them but probably each one will have 1 reference to 1 common/shared submodule like "components/common" which will contain some constants, for example, and no references to other files. At the top level of them you will have another index.js or .ts file that is referencing all of those components, so "components/index.js" will import and reexport all the nested components index files. So in order to import a Button, for example, you can either import "components/index.js" file with all the other imports this file is using, either only 1 single "components/button/index.js" file which is obviously much more easy to fetch. Just imagine a tree data structure, you import root of the tree (root index.js) - you get all the tree nodes. You import one specific Node (components/button/index.js) of the tree - just load all the childs (imports) of that node.
Sorry for a long read but asuming you mentioned webpack - there is a technique called tree-shaking which will cut off all the unused things.
Info about modules: https://www.w3schools.com/js/js_modules.asp
Info about Tree-Shaking: https://webpack.js.org/guides/tree-shaking/
It might not be as complicated as you think. Let's say you write the following library:
// your-library.js
const A = 22
const B = 33
export function getA () { return A }
export function getB () { return B }
export function APlusB () { return A + B }
// a lot of other stuff here
If some consumer of your library wants to make use of the APlusB function, they must do the following:
// their-website.js
import { APlusB } from 'your-library'
const C = APlusB()
However, depending on how the code is bundled, they may or may not wind up with the entire your-library file in their web bundle. Modern bundling tools like Webpack may provide tree shaking to eliminate dead code, but this should be considered an additional optimization that the API consumer can opt into rather than a core behavior of the import spec.
To make your library more flexible, you can split up independent functions or chunks of functionality into their own files while still providing a full bundle for users who prefer that option. For example:
// your-library/constants.js
export const A = 22
export const B = 33
// your-library/aplusb.js
import { A, B } from 'constants'
export default function APlusB () { return A + B }
// your-library/index.js
// instead of declaring everything in one file, export it from each module
export * from 'constants'
export { default as APlusB } from 'aplusb'
// more exports here
For distribution purposes you can package your library like so:
your-library
|__aplusb.js
|__constants.js
|__index.js
You mentioned react-bootstrap and you can see this exact pattern in their file structure:
https://github.com/react-bootstrap/react-bootstrap/tree/master/src
and you can see they aggregate and re-export modules in their index file here:
https://github.com/react-bootstrap/react-bootstrap/blob/master/src/index.tsx
Essentially, what you are asking is:
"How to export react components"
OR
"How are react components exported to be able to use it in a different react project ?"
Now coming to your actual question:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is 'Button' component is the default export of that file react-bootstrap/Button.tsx. So there is no need for destructuring a specific component.
If you export multiple components/ functions out of a file, only 1 of them can be a default export.
If you have only 1 export in a file you can make it the default export.
Consider the file project/elements.js
export default function Button(){
// Implementation of custom button component
}
export function Link(){
// Implementation of custom Link component
}
function Image(){
// Implementation of custom Image component
}
Notice that the Button component has 'default' as a keyword and the Link component doesn't.
The Image component can't even be imported and can only be used by other functions/components in the same file.
Now in project/index.js
import 'Button', {Link} from './elements.js'
As Button component is the default export its possible to import without destructuring and as Link component is a regular export, I have to destructure it for importing.
I'm following a nest tutorial and the instructor creates a folder called dtos and inside it creates two dto's (create-user.dto and edit-user.dto). Then, create an index file (in the same folder) that contains only the following:
index.ts:
export * from './create-user.dto';
export * from './edit-user.dto'
I don't understand two things:
1-why do you export the dtos from there? they already export themselves.
2- because it uses exports the dtos directly. Shouldn't I import them first?
Here is the code of the data:
edit-user.dto:
export class EditUserDto {}
create-user.dto:
export class CreateUserDto {}
1-why do you export the dtos from there? they already export themselves.
It allows for more concise importing. Say your folder structure is:
top
index
dtos
index
create-user
edit-user
If you import create-user and edit-user into dtos/index, and then export them from dtos/index, you can then import them from the top index with:
import { EditUserDto, CreateUserDto } from './dtos';
This is accessing what dtos/index exports.
Without this - yes, the classes are already exported, but importing them elsewhere takes a few more characters, since you have to navigate the folder structure more. From the top's index, you'd need:
import { EditUserDto } from './dtos/edit-user.dto';
import { CreaetUserDto } from './dtos/create-user.dto';
It's ever so slightly more unwieldy. Not a big deal. Some might prefer the extra boilerplate in order to import more concisely, others might prefer to navigate directly to the nested file location without bothering. Either will work just fine.
2- because it uses exports the dtos directly. Shouldn't I import them first?
You can import from a file and export that which you're importing in the same line using that syntax you see. export * from 'path' will take everything path exports, and export it in the current file as well.
I am using babylonjs in my React app. Since this file get bigger and bigger I try to split it into several parts.
Scene3d.js
import * as Setup from './Scene3d/setup'
class Scene3d extends Component {
...
}
export default Scene3d
setup.js
import Scene3d from '../Scene3d'
Unfortuantely I am not able to use the instance object of Scene3d. Scene3d just returns the component in setup.js. I want to avoid to add "this" to all function to pass the object to the functions.
I appreaciate your help.
Best
As far as I understood from the module system, whenever I import 'some_module' inside a file, I will always get the same instance of that module, and not a different instance on every import.
But if that's true, I kind of don't understand this pattern I've seen in some apps:
// in a 'config_some_module.js' file
import SomeModule from 'some_module';
SomeModule.attribute = 'something';
export default SomeModule;
// in a different file;
import SomeModule from './config_some_module';
If every time I import a module I get the same instance (and not a new instance), then why is it needed to re-export that module to access it with the configuration that was done on the previous file?
Also, a second question: if that's NOT needed, how to be sure that in the second file the import will get the module when that property is already set? I assume that if both imports get you the same instance, then eventually the property will be present in SomeModule on the second file, but maybe the pattern I mentioned above is usefull because you can be sure that the changes to the module were already applied?
The reason you need to export is because otherwise, config_some_module.js would only be creating a side-effect. If you want to import from it directly, you need to export a value. If you don't export anything from config_some_module.js, you'd need to import the modified object with the side-effect by doing this:
// in 'config_some_module.js' file
import SomeModule from 'some_module';
SomeModule.attribute = 'something';
// in a different file;
import './config_some_module'; // introduce side-effect
import SomeModule from 'some_module'; // access modified object
One "gotcha" to keep in mind is that the side-effect will only occur once, no matter how many times the config_some_module.js is imported.
Lastly, the order in which you execute the import statements in the consumer does not matter as long as your usage occurs after both.
What happens when you write an import statement in the following form:
import React, { Component } from 'react';
Is destructuring of the import module occurring as in destructuring an object to achieve Component instead of needing React.Component? Or is it importing with a named export with completely differing syntax, though it does resemble destructuring?
An important corollary question: does import React, { Component } ... needlessly load up the Component object from the React module twice as compared to simply import React ... (given that Component is a constituent of the larger React library)?
To answer your first question:
No, it's not object destructuring. The syntax may have been set up that way to relate but there's no confirmation that they were intentionally made to be related. Per the ECMAScript 2015 Language Specification:
Section 15.2.2 Imports
Syntax
[...]
ImportClause :
[...]
ImportedDefaultBinding , NamedImports
[...]
NamedImports :
{ }
{ ImportsList }
{ ImportsList , }
It's completely separate syntax.
To answer your second question:
Yes, it imports it twice, once React for access as React.Component by the default export, and once as Component as a named export. Per the specification:
Section 12.2.2 Static Semantics: BoundNames
[...]
ImportClause : ImportedDefaultBinding , NamedImports
Let names be the BoundNames of ImportedDefaultBinding.
Append to names the elements of the BoundNames of NamedImports.
Return names.
As you can see, the names you import with import React, { Component } are bound twice, meaning you get React as the default export and thus React.Component, and then the bound name Component is also appended to your imported names. You essentially get it twice under two different bindings or names.
It should be noted that only the bound names are different. React.Component and Component refer the same object, just with different bindings because you imported using named exports. Once you import React, React.Component has already been imported. All { Component } does is create a new binding to the already imported object.
There is no destructuring happening in the import syntax. Even though it looks somewhat similar - it's a separated syntax.
The imported identifiers are bindings to the objects created during module initialisation. So practically you get 2 bindings to the same object, which costs you 1 extra reference, nothing more.
No matter how many times in your source code tree you import a module it would only be initialised once, with all the values created just once. And all the import statements would essentially "bind" to the values in memory without creating duplicates.