Tips on React for large scale projects

Written by zaguiini | Published 2018/05/21
Tech Story Tags: react | front-end-engineering | software-development | large-scale-projects | tips-on-react

TLDRvia the TL;DR App

A vision about how to don’t get lost in your own application

It’s been two years time that I’ve met React and immediately fell in love with it. The way that React works, as a declarative, not-so-large library created exclusively to build rich User Interfaces provides everything as necessary (plus a rich ecosystem with tons of add-ons). As it’s read in the documentation, React is very unopinionated about how you might structure your project and choose your library stack, although the above mentioned document shows some recommendations.

This article was born to give enlightening recommendation for front-end developers on how you should organize your project if you want it to feel less painful and more scalable/clean as well. I don’t think that’s a rule, though. Feel free to use it, or, if you know a better way or have suggestions, just leave it in the comments! :)

Folder structure

In my current project, at the company for which I am working, I'm using something like this:

Wow!

You must be thinking: "what the heck?"

I know. That's a lot of stuff going on. Don't worry: we'll get through it.

Lets start from the top:

/api

Pretty self-explanatory. Should serve as the folder to place functions that call external resources (usually REST apis). The users. file must have an export default statement, for example.

/assets

Explains itself as well. Not much to do. If you're working with webpack and create-react-app, should be easy to just import it and bundle within your JS or extracted to their own files in the public folder.

/components

The global/common components folder. Every component that might be used in two or more views are put in this folder. Every component has its own folder, index.js and styles.module.scss for the component styles. I'll come on the styles.module.scss part later. For now, just keep reading.

I prefer to keep components simple, declared as arrow functions. This way, I somehow manage to "polite myself" against abusing the component responsibility (i.e. overusing state). It's a rare case when components do use state, but sometimes it is necessary. Also, some may include nested components (like a dropdown menu or so).

/lang

Now, this part is recommended only if you're thinking about internationalizing your React app. Later on I'll talk about the stack, but it's important for now: I'm using i18n-js as it's quite easy to use. The index.js of the lang folder looks like this:

The keys folder has the constants in order to prevent typos and get them on-the-fly with a Linter, for example. A basic keys‘s folder index.js file would be:

And, in the auth.js file:

With that defined, when you want to use a localized message, just import the default function, the desired key and then invoke that function. Like this:

And let's not forget to define the translations in the corresponding folders. I'll just make it in english for the sake of brevity:

And now, in the en/auth.js file:

There's a clear explanation on this Stack Overflow answer about why you should use constants. Enough for i18n. Lets move on!

It's the same image. I'm saving you from some scroll time!

/lib

Now that's something that is not entirely related to React, but I, particularly, like to have a lib folder to put stuff like validation, some helpers/utils, etc. Some might argue that the api folder may be there as well. Fair enough! It's up to you.

/store

This folder is only needed if you're using Redux. I don't know much about MobX — never used — but it should be different.

Keep in mind that, while Redux is a great addition, you might not need it.

These subfolders should be pretty self-explanatory, but there are a few gotchas. Bear with me!

/actions: the action creators folder. In that folder we must put the functions that are bound to the connected component.

/definitions: that are basically the "action types" constants. But I prefer to call it "definitions" and put it outside the actions folder because those definitions might be used in the reducers and in the sagas as well.

/reducers: the reducers folder. Every reducer should have its own file and the index.js file is responsible for combining them into one.

/sagas: the same thing as the reducers, but for the sagas. The index.js file contains the rootSaga, the saga responsible for wiring it all up. Example:

NOTE: It's ok to not use Saga if you don't want to. Not obligatory!

With our reducers and sagas set up, it's time to create our store and that's defined in the store folder's index.js:

This way, it will become a pleasure to work with centralized state! Cheers! Time to move on :D

/styles

That works exactly the same way like the components folder, but for the common styles across components or views, like your grid system for example!

/views

For the components folder we defined our visual — or presentational — components, which rarely rely on state and/or another components orchestration. In the views folder, however, we define our container components, which will serve as, you guess: the views for our application. As you might think, these components most of the time rely on state, so no arrow functions here and extending from React.PureComponent instead of React.Component is recommended!

These views might have children components. And it's fine to declare them in the related view folder in order to avoid defining that much "global components". I usually move the component to the components folder if I use it in two views or more.

For the index.js file of the views folder: most of the times I define it as the Main component in my app, because it usually contains the app's router. Example:

Done with the views! Time for our last file: the src's folder index.js:

Now, it should be obvious that the Provider stuff is only needed if you want to bind Redux to your app. Just ignore if you don't. And there you have it: a very well organized folder structure and app! You should be able to render it in the DOM by calling ReactDOM.render by now!

Before talking about the stack (a.k.a. libraries that I use and I would like you to use), let's talk about CSS.

The React's CSS war

You know, we know, that are tons of posts and libs to work with CSS in React. I like to work with CSS modules. It's CSS on steroids and I can use with SASS as well, so I really don't see the reason to switch for Styled Components and others. I'll not teach you how to work/install CSS modules in this article. But read this if you want to.

Buuuuuut… I've made something different. While I love to work with CSS modules, sometimes it just doesn't fit: for global styles of third-party components, for example. So, I'm working with modules, and still working with globally-imported styles with this configuration on my [webpack.config.js](https://gist.github.com/zaguiini/174122d57c6c933dd7fd1b01ab8e6d5b) file Gist. Check it out!

I only work with SASS. Not interested in making it work with plain CSS, but it should be almost the same configuration. Now, in my components, I do it like that:

Ok, that's a contrived example, but it should show exactly what I'm saying, and using the amazing [classnames](https://www.npmjs.com/package/classnames) together.

Finally: the basic stack!

Now, there are some libraries that I like to use and which I found pretty helpful:

  • Always the latest react and react-dom, of course;
  • classnames for painless class names declaration;
  • i18n-js for internationalization;
  • color for color manipulation;
  • lodash or underscore for a set of utility functions (that would possibly make our lib/utils folder unnecessary);
  • ramda instead of lodash/underscore if you're totally into pure functional programming and composition;
  • If you liked ramda, you're probably going to like recompose as well (good for composing components from pure functions);
  • dinero.js for money manipulation/display;
  • moment or date-fnsfor date/time manipulation/display (I'd go for the latter because moment is not pure and that just ain't good — thanks for the heads up, Marcelo Camargo);
  • react-helmet for <head> tags manipulation;
  • Both react-table and react-virtualized, because sometimes one is just simpler than other and react-table fits well when you don't want to display a ridiculously big amount of rows. Also, virtualized looks more like a list renderer than of a table;
  • react-redux, redux, redux-saga and redux-logger for centralized state. Those two middlewares play well together. A must!
  • react-router-dom if you're building for the web, and react-navigation if you're using React Native.

Now that should keep you up and running for a good large scale project! This article is pointed towards React DOM, but with some tweaks you should make it work with React Native!

Thanks for reading!


Published by HackerNoon on 2018/05/21