Three Habits to Break on Large React Native Projects

Working on a large(ish) React Native project presents some unique challenges for web and mobile developers. Isolating components from one another is a great way to make refactoring easy, but it leads to some pretty gnarly directory hierarchies.

If you read much React Native code, you’re bound to pick up a few habits that cause pain in the long run. Here are three bad habits that you should try to break on your next React Native project.

Habit 1: Shake It Off

When you’re working with a React Native app, you’ll often need access to the platform’s built-in debug menu. By default, this is triggered by shaking your phone. When working with a simulator, that’s pretty easy. Both the iOS simulator and the Android emulator have keyboard shortcuts for triggering a virtual shake (^⌘Z, and ⌘M, respectively).

But when testing on real devices, bringing up the debug menu can be a bit more of a pain…literally. Most iOS devices that we test with are good at registering a subtle shake gesture. The Android devices though, especially the older, slower devices that we like to use for testing, aren’t as great at listening.

After a few days (okay, weeks) of flailing around like cats on ice trying to bring up the debug menu on our Android test devices, we looked around and found an alternative. ADB can press any key on a connected Android device, including the (not always physically present) menu button.

Virtually pressing that button does exactly the same thing as a shaking gesture would in a React Native context. To make things easier, we added a simple script to our project repo, and now we can run make android_debug_menu, or more often, the aliased version m adm, to bring up the debug menu on an Android phone.

Much nicer. Much less flailing.

Habit 2: Index All the Things!

Technology is always a palimpsest. When something new comes along, its authors conciously and unconciously adopt some of the “I dunno, I guess we could…” solutions of whatever came before. In time, strange things happen.

Generations of web developers absorbed the idea that index.html was a natural, normal part of web development. Thus, they passed on the idea that “index” as a filename was somehow important (it’s not). Unleashed on mobile via the magic of React Native and TypeScript, those web developers followed a similar pattern. Only this time, instead of index.html, they called the magic files index.tsx.

In the first phase of our RN project, we used index.tsx liberally. Every component that was larger than a hundred or so lines got put into a folder that bore its name (like my favorite: full-screen-starry-background-view/) and renamed to index.tsx. Pure functions that didn’t need to depend on props were split out into a utils.ts file, and we put especially long stylesheets into styles.ts.

It was wonderful. Whenever I browsed around the repo using the tree view in VS Code or in Finder, each component felt the same. I knew immediately where to go.

Unfortunately, navigating code using the tree view isn’t common. Once the folder structure of our app settled down a bit and architectural work gave way to feature development, we noticed ourselves using mostly fuzzy finding to open files. To open the FullScreenStarryBackgroundView component, for instance, we wouldn’t visually scan the tree view. We’d type something like ⌘P, fssbvind, return.

The ind at the end became a reflex, something we’d do without thinking. The real pain came when one of us would have a few different components open at once. It’s not uncommon, especially during a refactor, to have a reference component open in one pane while editing a new component in the other. Glancing at the tabs, we’d see something like this:

Most people have enough spatial memory to handle that situation, but that’s a simple case. We don’t obsessively close files when we’re done with them. That’s the computer’s job. This, then, is a more common case:

Heaven help you if you get distracted and find yourself trying to visually orient with the tab bar. Good luck.

To alleviate the pain, we broke the habit of index and established a naming convention: the primary file in any component folder shares the name of its folder. Instead of something/index.tsx, we have something/something.tsx. This vastly increased the number of semi-gross-feeling import {…} from ‘…/something/something’ statements in our repo, but it also made the tab bar soooo much more useful.

It’s a tradeoff, but in our minds, a good one.

3. Tyrannical Defaults

TypeScript and JavaScript both have a really handy feature that lets you specify one thing from module as a default export. Anyone using the module can import that thing without knowing its name by using import Whatever from "your-module". This saves us from typing awkward curly braces around every import and makes fixing name collisions from libraries super-easy.

Want to import _ from lodash and _ from something else too? Cool. Just change the import _ to import _ as Whoa in one of the imports, and you’re golden.

TypeScript has another other super, super-nice feature. If you use an explicitely named module somewhere in your file, your editor can automatically add the import statement for that export. This turns out to be especially nice when working on a large React Native app. Most of the time, when I want to pull in a component, I can just type its name (or something close enough that VS Code’s autocomplete knows what I mean), hit return, and move on with my day without sparing a thought for the import statement.

Trouble is, you can’t use these features together. Since default exports don’t have a name, TypeScript can’t automatically import them for you. Initially, we used default exports all over the place, because that’s what all the cool libraries were doing. But in doing so, we cut ourselves off from a really nice productivity feature.

We broke Habit 3 and established a rule that all exports must be named. If you feel nostalgic and want to also export something as default, that’s fine, but the thing must be accessible as a named export, too.

To recap:

  1. Stop shaking your phone.
  2. Name your files something other than index.
  3. Name all of your exports.

We follow these counter-habits on our React Native project. Doing so has made our lives quite a bit nicer. How do you make sense of modern toolchains? I’d love to hear your strategies in the comments.