I have the following structure for my JS components:
/components
/Menu
/Menu.js
/Menu.test.js
/index.js
/MenuItem
/MenuItem.js
/MenuItem.test.js
/index.js
The idea being we can have a folder for each component that can contain a test (and possible other files) and then if we just want to import the component we can rely on the use of the index.js to handle the directory import without having to reference the class inside the folder direct.
So for example:
Menu.js
class Menu extends Component {
// ... more code ..
}
export default Menu;
index.js
export default from './Menu';
And then it can be used like:
import Menu from './components/Menu';
However I found that this didn't work and it couldn't find the module...
So to fix this I had to import Menu into the index.js before export:
import Menu from './Menu';
export default Menu;
But having looked at how other projects have structured their code, I have seen that they are using the former without having to import again...
For example: https://github.com/IBM/carbon-components-react/blob/master/src/components/Breadcrumb/index.js
See how they have exported from the Breadcrumb without having to do the import first... how have they achieved this?
If you check the provided example, you will see they use
this babel plugin.
So you can add one to your babel plugin stack.
Alternatively, you can look into using just export {default} from "./destination"; form which works as is.
Related
I am working with a React project where each component's files are contained in their own directory. I have a component.jsx file and an index.js file that re-exports the component's default export. This works as expected. I would like to simplify my import statements by re-exporting all components up directory level from the main components folder. See below for an example of my current project foloder structure.
src
--components
----Alert
------Alert.jsx
------index.js
----LoginCard
------LoginCard.jsx
------index.js
--index.js
Alert/Alert.jsx
export default function Alert(props) {
// omitted for brevity - the component itself works fine
return <Alert />
}
Alert/index.js
export { default } from './Alert';
At this point, imports work as expected in the LoginCard component.
LoginCard.jsx
import { UserContext } from '#contexts';
import Alert from '#components/Alert';
export default function LoginCard(props) {
// omitted for brevity. Again, component works as expected
return <LoginCard />
In order to achieve my desired end result of simplifying import calls, I created components/index.js:
components/index.js
export { default as Alert } from './Alert';
Which I then attempt to import as:
LoginCard.jsx
import { Alert } from '#components'
When attempting to import from component/index.js as import { Alert} from '#components' I receive an exception that states "cannot read default property of undefined". What makes this strange is that I import/export my pages and contexts in exactly the same manner and they work as expected. I originally thought that this implied an issue with my components directory alias, but the alias is working as I can import just fine from #components/Alert, just not from #components
Have a missed something simple, or am I hitting a bug? Thanks.
I think the issue here is that you are attempting to push all the exports up the tree towards the root directory. This makes sense when importing somewhere outside that root directory. The issue lies in the fact that you are attempting to import from the root while inside the directory structure. In other words, all the exports from within the directory need to be processed before anything can be exported from the root.
The snag here is that you are attempting to import Alert from the root export from LoginCard... which while being processed hasn't finished its exports, so the root export isn't ready yet.
In other words, #components/Alert is ready, #components is not.
Just use a relative import of Alert (and any other import) from within the same components directory.
import { UserContext } from '#contexts';
import Alert from '#components/Alert';
// or
import Alert from '../Alert';
export default function LoginCard(props) {
// omitted for brevity. Again, component works as expected
return <LoginCard />
I have a files like this:
components/carousel.js
import hammerjs from 'hammerjs';
import React from 'react';
export default () => <div>...</div>;
components/layout.js
import React from 'react';
export default () => <div>...</div>;
components/index.js
export { default as Carousel } from './carousel';
export { default as Layout } from './layout';
Now in my server side rendered app I import layout like so:
import { Layout } from './components';
I get an error about window not being defined, because it's reading through components/index.js and seeing hammerjs dependency inside the Carousel export, which requires window and isn't available on the server.
Why is it reading the code in the Carousel component when I'm only trying to import the Layout component? How do I avoid this happening?
In short - why is it reading the Carousel component?
You are exporting two defaults in your components/index.js which is not a supported operation. Export the named components individually or one default objet that holds both.
1) From MDN on import/exports - You can have multiple named exports per module but only one default export. link
In your components index file you're exporting both as default - export the named variable. Then import the named variable.
2) The HammerJS dependency shouldn't matter. Your react code will be run through some sort of transpiler that will take the node modules and turn them into something the browser can understand.
We're using a folder structure like this
components
| Button.js
| Nav.js
| ...etc
| index.js
somefolder
|somefile.js
in the inderx file we're importing every component and reexporting it like this
// index.js
import Button from './Button'
import Nav from './Nav'
export {Button, Nav}
this way we can import many components into a file like this
// somefile.js
import {Button, Nav} from '../components'
Maintaining that index file is a bit of a pain though and discourages flexible use of components. I know that Webpack can import many files with a syntax like this
function requireAll(r) { r.keys().forEach(r); }
requireAll(require.context('./components/', true, /\.js$/));
however, I didn't yet find a way to reexport all of these components to use them like above.
The desired outcome is to replace the index.js file with something that automates the process of bundling all the files from a folder without having to add every file manually.
I think getting rid of the file index will not be the best solution and may cause questions from other developers. But I can offer a slightly more simplified way:
index.js:
export * from './some-component1.js';
export * from './some-component2.js';
some-component1.js:
export {SomeComponent1};
some-component2.js:
export {SomeComponent2};
For cases like this,
/ACollectionOfTinyComponent/index.js
import Container from './Container';
export {
Container,
};
So the index.js becomes a directory to include other small parts without having to write each Component Name out all the time.
So in this case, we can import a component in another component like following:
import {Container} from './ACollectionOfTinyComponent'
//then use Container in the code here
Is this a bad practice? Since if I have airbnb linter enable then I got error linting of
Prefer default export import/prefer-default-export
it asks me to add default but that will cause compile error
I found out that because I added only ONE import & export for the index.js. But if I add more than one its fine!
For example, if I do
import Container from './Container';
import Ezeewei from './Ezeewei';
export {
Container,
Ezeewei,
};
Note that I added one more import of Ezeewei.
Then the linting rule will pass!
it asks me to add default but that will cause compile error
You must be using the wrong syntax for exporting a default.
export { name1 as default, … };
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Syntax
import {Container} ... Is this a bad practice?
Yes. If you have a single entry point into a component (like a a class), you should export it as the default rather than a named export. The designers of the language gave default imports and exports a special syntax to promote it as the primary use case.
http://2ality.com/2014/09/es6-modules-final.html#default-exports-are-favored
I'm trying to figure out an issue I have with ES6 modules import.
This is a very simplified version of what I'm attempting to do. My current file structure is much more complicated with nested folders.
I have 2 ReactJS components:
/buttons
/MyComponent1.js
/index.js
/texts
/MyComponent2.js
/index.js
/index.js
My files look something like this:
I'm importing the MyComponent2 from the root index.js file which is the entry-point of my package.
MyComponent1.js
import MyComponent2 from '../../';
export default () => (
<div><MyComponent2 /></div>
);
MyComponent2.js
export default () => (
<div>Hello world</div>
);
My buttons/index.js and texts/index.js files export all components in their own folder:
buttons/index.js
export { default as MyComponent1 } from './MyComponent1';
texts/index.js
export { default as MyComponent2 } from './MyComponent2';
And my root index.js export all my folders to make them publicly available. It's my entry-point:
export * from './buttons';
export * from './texts';
So when I import MyComponent2 in MyComponent1 from the root index.js file, it's undefined. When I import it from ./MyComponent2.js, it's defined. When I import the entire package in another project and import MyComponent2 from the root index.js file, it's defined.
It's mind-blowing and I don't understand why I can't import MyComponent2 from the root index.js file.
I need to do that because I have a set of hundred of components nested in different folder and I want to make them all available from this entry-point file.
Can someone tell me what is happening and how to make this possible?
Thanks
Okay, took me a while to figure out what is happening. Please take a look at the sandbox i've setup - https://codesandbox.io/s/nk0qmo096j
I've tried to keep the folder/module structure similar to what you've described. Please look through how the folder/modules are structured before you continue reading.
When you run the code in the sandbox, take a look at the console. You'll see something like this -
So let me try to explain why what you're trying to do is not working
buttons/MyComponent is the first file to be "exported" (since its at the bottom of the dependency chain; Project/index -> components/index -> buttons/index -> buttons/MyComponent)
Note that components/index has not been exported yet, so it will return undefined
Then buttons/index is exported, followed by texts/index
Then buttons/MyComponent is rendered; just before it is rendered i manually require components/index and it is now defined so returns some defined value
In essence, when your MyComponent1 is trying to import MyComponent2 via the index file, the index file has not been exported yet so it returns undefined. To work around this, you have to require MyComponent2 from within MyComponent1's render method.
you can import all the components from the respective component files and then assign them to a variable in the root component and then export all of them as objects.
import MyComponent1 from './buttons/index'
import MyComponent2 from './texts/index'
export {MyComponent1, MyComponent2}
to import this components in another file or project. you could import only one them using object destructuring.
import {MyComponent1} from 'index.js'
import {MyComponent2} from 'index.js'
In my case, I had an old js file that the IDE imported instead of the updated ts file. so if your module file is module.js make sure there isn't a compiled old module.js in the same directory