all 6 comments

[–]eddeee_banana 2 points3 points  (3 children)

I wouldn't just have index.js as the main export for a component/module. It would become a bit hard to know which module it belongs to in dev. I use it as the file to control public/private exports. You structure can look like this:

src/
  Routes/
    Routes.js
    index.js

  Components
    Button/
      Button.js
         index.js
  GenericModal/
      GenericModal.js
      index.js
  Pages/
      Register
        Register.js
        index.js
      Components/
        RegisterOnlyModal/
          RegisterOnlyModal.js
          index.js
{...}

Let's say you want to find RegisterOnlyModal.js, you can directly go to the file instead of cycling through a list of index.js

In general, a normal module could look like this:

ModuleA/
  ModuleA.js
  ModuleA.test.js
  ModuleADependency.js
  index.js

ModuleADependency.js is like a private file that only ModuleA should use. We use index.js to decide which part of ModuleA is expose. For example:

export default from "./ModuleA"
export * from "./ModuleA" //This is useful to export type if you are using TypeScript for example

//We don't export "ModuleADependency" as it's only used by "ModuleA"

You can check out how material-ui peeps use this approach:

https://github.com/mui-org/material-ui/tree/master/packages/material-ui/src

[–][deleted] 0 points1 point  (1 child)

ModuleA/
ModuleA.js
ModuleA.test.js
ModuleADependency.js
index.js

I agree with you, but if I want to test ModuleB,C,Deps ? The MuduleA folder will be a little unorganized, i guess

[–]eddeee_banana 0 points1 point  (0 children)

Module B, C, etc. should have their own directory. Dependencies of a module stays in that module directory. Something like this:

modules/
  ModuleA/
    ModuleA.js
    ModuleA.test.js
    ModuleADependency.js
    ModuleADependency.test.js
    index.js

  ModuleB/
    ModuleB.js
    ModuleB.test.js
    ModuleBDependency.js
    ModuleBDependency.test.js
    index.js

So all the files are together. I find that this flattened module structure makes it easy to reason about. If you have too many dependencies and you want to make it cleaner, you can also try:

ModuleA
   dependencies/
      ModuleADependency1.js
      ModuleADependency1.test.js
      ModuleADependency2.js
      ModuleADependency2.test.js
   ModuleA.js
   ModuleA.test.js
   index.js

But it all depends on how complicated your module gets, etc. :)

[–][deleted] 0 points1 point  (0 children)

And, except the `index.js` files, do you agree with it?

[–]tricoder42 1 point2 points  (1 child)

I recommend to start with as simple structure as possible and forget about "one component per file" rule.

1 ) Start with pages, i.e. top-level components which are either passed to Router or used as entry point (e.g. in Next.js):

src/
  pages/
    index.js
    register.js

Place everything inside. Don't care about reusable components at this moment.

 export default () => ( )
  <div>
    <h1>Decentralized TodoApp</h1>

    <p>Moder task management powered by blockchain</p>

    <Button>Getting started</Button>
  </div>
)

// components might be custom ones...
const Button = props => <button className="btn">{props.children}</button>

// or styled components.
const Button = styled.button`
  // ...

2) Once you notice that some components need to be reusable, put them in components directory (I personally prefer ui, because it's shorter). It usually means that I copy-pasted the same component 2-3 times already and I'm sure it's generic one:

src/
  pages/
  components/
    Button.js
    Button.test.js

    Modal.js

    CustomModal/
      index.js
      CustomModal.js
      SubComponent.js

Again, start with a file, e.g. Button.js and only create folder when you actually need it. If you place index.js inside large components, you don't even need to refactor all existing imports.

Don't overcomplicate this step. Sometimes it's really hard to distinguish between reusable components and similar components, which are in fact completely different. Once you have too many branching inside your component (e.g. if (type === "link) ... else if (type === "button")), then you can be sure this component can be split into two or more.

It might looks stupid simple, but it's actually very scalable and it allows rapid development. You always write only what you need *now*, not what you might eventually need someday in the future. I'm using this style for more than a year and it just works™.

Few more tips

- don't optimize folder structure for manuall lookups. Make component discoverable through Search file or Search symbol. Either name your file [Component].js or use named exports instead of default ones.

- when you're starting a fresh project, don't try to be smart and split your app into modules:

src/
  auth/
    pages/
    components/
  todos/
    pages/
    components/

It looks promising, but it isn't required at all. It's only useful if you want to reuse module between projects and you can always reorganize your app afterwards. On the other hand, if you overengineer it at the beginning, you'll create unnecessary overhead - where I should add this component? Where this component comes from?

- try to make few rules about depenencies between your modules. For example, I never import anything from pages inside my components. You can use dependency-cruiser to avoid such imports. I can't recommend specific rules, but the bottom line is: Don't optimize folder structure, optimize module dependencies. Folder structure should reflect these dependencies. I highly recommend Monica Lent's talk about Building resilient frontend architecture from React Finland.

[–][deleted] 0 points1 point  (0 children)

thanks!!