August 12, 2019
I’ve developed a peculiar habit. I knew I’d developed it, but didn’t know it was peculiar until my lead asked me about it this morning.
When organizing my files, I’m quick to add a subdirectory to house even a single module.
Here’s an example of a folder structure for my
Property directory. It’s a fairly typical example of this habit:
$ tree . ├── InteriorFeatures │ ├── InteriorFeatures.tsx │ └── index.tsx ├── Location │ ├── Location.tsx │ └── index.tsx ├── Lot │ ├── Lot.tsx │ └── index.tsx ├── Rooms │ ├── AddRooms.tsx │ ├── Rooms.style.tsx │ ├── Rooms.types.ts │ ├── RoomsSummary.tsx │ └── index.tsx └── Structure ├── Structure.tsx └── index.tsx
My reasoning for this structure was two-fold:
indexfile since I find them difficult to locate later (searching
indexin an IDE can yield a lot of files if components are written this way)
import Lot from "./Property/Lot/Lot"
In my aim to organize my code, I’d created a lot of overhead and extra files. Directories that didn’t need to exist and two line index files: For example,
./Location/index.tsx is a typical example:
import Location from "./Location"; export default Location;
I’d gotten in this habit because of how nice it made my import statements.
import Location from "./Property/Location"; import Lot from "./Property/Lot"; import AddRooms from "./Property/Rooms"; import Structure from "./Property/Structure"; import InteriorFeatures from "./Property/InteriorFeatures";
After discussing the topic for a few minutes, my lead shared the Node documentation on Modules. It wasn’t my first time looking, but it’s always informative.
This time, I started stepping through the Pseudocode the Node team provided on how
require works: 1
(NB: while there are differences between
import in this context my understanding is that they do not apply)
Since my imports are relative, they fall under step 3:
3. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X)
If all I did was delete the
index file, but retain the directory structure, the app wouldn’t be able to compile because it would try to import as an entire file.
For example - a refactored
$ tree . ├── InteriorFeatures │ ├── InteriorFeatures.tsx …
import InteriorFeatures from ‘./Property/InteriorFeatures’;
Going through our Pseudocode, we’d end up in
LOAD_AS_FILE with X as the value of
Property/InteriorFeatures/InteriorFeatures — this is a
.tsx compiles to
.js), so we know we’d load it as JavasScript text.
What I really want is to import the file and then use the default export within it.
So, deleting the
index.tsx and calling it a day won’t work. But, I can lift the files out of the subdirectory altogether and delete the extra folder, which, after all, serves no real purpose since the folder isn’t actually organizing anything for all of these modules (with the exception of
Understanding this, my new folder structure is as follows:
$ tree . ├── Rooms │ ├── AddRooms.tsx │ ├── Rooms.style.tsx │ ├── Rooms.types.ts │ ├── RoomsSummary.tsx │ └── index.tsx ├── InteriorFeatures.tsx ├── Location.tsx ├── Lot.tsx └── Structure.tsx
My import statements remain pretty, but without the extra cruft of unnecessary directories or
Understanding how require (and import) work allow for more intuitive grouping of modules and can avoid unnecessary clutter. Using this understanding, I’m avoiding housing logic within
index files which are predominantly reserved for routing, maintaining streamlined import statements, and eliminating excess files.
Thanks for reading! My name's Stephen Weiss. I live in Chicago with my wife, Kate, and dog, Finn.
Click here to see the archives of my weeks in review and sign up yourself!