Wednesday, April 26, 2017

How to Organize a Large React Application and Make It Scale

This article is by guest author Jack Franklin. SitePoint guest posts aim to bring you engaging content from prominent writers and speakers of the Web community

In this article, I'll discuss the approach I take when building and structuring large React applications. One of the best features of React is how it gets out of your way and is anything but descriptive when it comes to file structure. Therefore you'll find a lot of questions on StackOverflow and similar asking how to structure applications. This is a very opinionated topic, and there's no one right way. In this article, I'll talk you through the decisions I make when building React applications: picking tools, structuring files, and breaking components up into smaller pieces.

Build Tools and Linting

It will be no surprise to some of you that I'm a huge fan of Webpack for building my projects. Whilst it is a complicated tool, the great work put by the team into version 2 and the new documentation site make it much easier. Once you get into Webpack and have the concepts in your head you really have incredible power to harness. I use Babel to compile my code, including React-specific transforms like JSX, and the webpack-dev-server to serve my site locally. I've not personally found that hot reloading gives me that much benefit, so I'm more than happy with webpack-dev-server and its automatic refreshing of the page.

I also use the ES2015 module syntax (which is transpiled through Babel) to import and export dependencies. This syntax has been around for a while now and although Webpack can support CommonJS (aka, Node style imports), it makes sense to me to start using the latest and greatest. Additionally, Webpack can remove dead code from bundles using ES2015 modules which, whilst not perfect, is a very handy feature to have, and one that will become more beneficial as the community moves towards publishing code to npm in ES2015.

Configure Webpack's modules resolution to avoid nested imports

One thing that can be frustrating when working on large projects with a nested file structure is figuring out the relative paths between files. You'll find that you end up with a lot of code that looks like this:

import foo from './foo'
import bar from '../../../bar'
import baz from '../../lib/baz'

When you're building your app with Webpack you can tell Webpack to always look in a specific directory for a file if it can't find it, which lets you define a base folder that all your imports can become relative to. I always put my code in a src directory. I can tell Webpack to always look in that directory. This is also where you need to tell Webpack about any other file extensions that you might be using, such as .jsx:

// inside Webpack config object
{
  resolve: {
    modules: ['node_modules', 'src'],
    extensions: ['.js', '.jsx'],
  }
}

The default value for resolve.modules is ['node_modules'], so you have to add it too else Webpack won't be able to import files that you've installed with npm or yarn.

Once you've done that you can always import files relative to the src directory:

import foo from './foo'
import bar from 'app/bar' // => src/app/bar
import baz from 'an/example/import' // => src/an/example/import

Whilst this does tie your application code to Webpack, I think it's a worthwhile trade-off because it makes your code much easier to follow and imports much easier to add, so this is a step I'll take with all new projects.

Folder Structure

There is no one correct folder structure for all React applications - as with the rest of this article, you should alter it for your preferences - but the following is what's worked well for me.

Code lives in src

To keep things organized I'll place all application code in a folder called src. This contains only code that ends up in your final bundle, and nothing more. This is useful because you can tell Babel (or any other tool that acts on your app code) to just look in one directory and make sure that it doesn't process any code it doesn't need to. Other code, such as Webpack config files, lives in a suitably named folder. For example, my top level folder structure often contains:

- src => app code here
- webpack => webpack configs
- scripts => any build scripts
- tests => any test specific code (API mocks, etc)

Typically the only files that will be at the top level are index.html, package.json, and any dotfiles, such as .babelrc. Some prefer to include Babel configuration in package.json, but I find those files can get large on bigger projects with many dependencies, so I like to use .eslintrc, .babelrc, and so on.

By keeping your app code in src, you can also use the resolve.modules trick I mentioned earlier which simplifies all imports.

React Components

Once you've got a src folder, the tricky bit is deciding how to structure your components. In the past, I'd put all components in one large folder, such as src/components, but I've found that on larger projects this gets overwhelming very quickly.

A common trend is to have folders for "smart" and "dumb" components (also known as container and presentational components), but personally I've never found explicit folders work for me. Whilst I do have components that loosely categorize into "smart" and "dumb" (I'll talk more on that below), I don't have specific folders for each of them.

We've grouped components based on the areas of the application that they are used, along with a core folder for common components that are used throughout (buttons, headers, footers - components that are generic and very reusable). The rest of the folders map to a specific area of the application. For example, we have a folder called cart that contains all components relating to the shopping cart view, and a folder called listings that contains code for listing things users can buy on a page.

Categorizing into folders also means you can avoid prefixing components with the area of the app that they are used for. As an example, if we had a component that renders the user's cart total cost, rather than call it CartTotal I might prefer to use Total because I'm importing it from the cart folder:

import Total from 'src/cart/total'
// vs
import CartTotal from 'src/cart/cart-total'

Continue reading %How to Organize a Large React Application and Make It Scale%


by Jack Franklin via SitePoint

No comments:

Post a Comment