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! :)
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:
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.
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.
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).
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!
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.
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
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!
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.
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.
Now, there are some libraries that I like to use and which I found pretty helpful:
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;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-fns
for 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;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!