How should we structure our React code? (2/2)
Tags: javascript, react, productivity, beginners
Published: 2020-01-09 11:44:45

*Note: This post doesn't portray that this is the only way to structure the code. There are many other great ways to do it. This method might have already been followed by most of you. This post also considers that you have some background knowledge about React and its ecosystem, also some common terminologies used in the web community*

### This is the second post of two post series. If you haven't read [PART-1](https://dev.to/surajjadhav/how-should-we-structure-our-react-code-1-2-1ecm) yet then go ahead.

I apologize for being late to post this. I binge-watched *The Office* series and wasn't able to resist to finish it before writing this down.

Let's continue our journey to learn how to build a well-defined file structure for your Reac SPA or any of your favorite web-stack.

## Continued...

So if you remember this is what our App component looks like,

```react
import React from 'react';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";
import './App.css';
import routeConstants from 'shared/constants/routes';

const {
  LOGIN,
  DASHBOARD,
  LISTING,
  PROFILE,
} = routeConstants;

function App() {
  return (
    <Router>
      <div className="App">
        <h1>App Component</h1>
        <ul className="App-nav-list">
          <li className="App-nav-item">
            <Link to={LOGIN.route}>{LOGIN.name}</Link>
          </li>
          <li className="App-nav-item">
            <Link to={DASHBOARD.route}>{DASHBOARD.name}</Link>
          </li>
          <li className="App-nav-item">
            <Link to={LISTING.route}>{LISTING.name}</Link>
          </li>
          <li className="App-nav-item">
            <Link to={PROFILE.route}>{PROFILE.name}</Link>
          </li>
        </ul>
        <Switch>
          <Route exact path={LOGIN.route}>
            <h1>{LOGIN.name}</h1>
          </Route>
          <Route path={DASHBOARD.route}>
            <h1>{DASHBOARD.name}</h1>
          </Route>
          <Route path={LISTING.route}>
            <h1>{LISTING.name}</h1>
          </Route>
          <Route path={PROFILE.route}>
            <h1>{PROFILE.name}</h1>
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

export default App;

```

I will create a Navbar component that will render the nav links in our App component. So let's create a file Navbar.js in `src/screens/App/components` directory.

![Code-File-1](https://thepracticaldev.s3.amazonaws.com/i/1klvw6166e732tvgx4lz.png)

Also making subsequent changes in App component file as well to import Navbar and use it,

```react
// Other import statements

import Navbar from "./Navbar";

const {
  LOGIN,
  DASHBOARD,
  LISTING,
  PROFILE,
} = routeConstants;

const navItems = [LOGIN, DASHBOARD, LISTING, PROFILE];

/* Inside return statement */

        <h1>App Component</h1>
        <Navbar navItems={navItems} />

/* other jsx code */

export default App;

```

The next thing we should do is give each route component their own space in our file structure. For that, I will create a directory named `screens` under `src/screens/App` directory.

I used the word `screens` here in my file structure is because our Application is a set of things that we see on our screens, it is easier for me or anyone to relate/understand that word. You can use any word you want like routes/children/views

I didn't use `views` because our views can differ. :P Nope that's not the reason. Maybe because I am taking Micheal Scott a bit too seriously.

![File-Structure-1](https://thepracticaldev.s3.amazonaws.com/i/xpml74rel4gwrhdl4v6z.png)

So now the `src/screens/App` directory contains two folders `components` and `screens`. The newly created screens contain 4 directories for all our routes, the Login, Dashboard, Listing, and Profile routes. Now, let's create a folder named `components` and a file `index.js` in each of the newly created folders.

![File-Structure-2](https://thepracticaldev.s3.amazonaws.com/i/kmkgyxm2d4us1ls8g0fr.png)

Then, I went ahead and create a component file for each route and fill it will a header tag and then exported that component in `index.js` as given below,

This is the component file contents
```react
import React from 'react';

function Dashboard() {
  return <h1>Dashboard</h1>;
}

export default Dashboard;
```

This is the `index.js` to export the above component,
```js
import Dashboard from './components/Dashboard';

export default Dashboard;
```

I am considering you did this for Login, Listing and Profile too.

Now I will import these route components in the App component and use them in rendering.

```react
/* Other imports */
import Login from '../screens/Login';
import Dashboard from '../screens/Dashboard';
import Listing from '../screens/Listing';
import Profile from '../screens/Profile';

/* Other App component code */

<Route exact path={LOGIN.route}>
    <Login />
</Route>
<Route path={DASHBOARD.route}>
    <Dashboard />
</Route>
<Route path={LISTING.route}>
    <Listing />
</Route>
<Route path={PROFILE.route}>
    <Profile />
</Route>

/* Other code */

export default App;
```

Okay now let's move router, routes and routing configuration into a separate space of itself. This will make sure our component files remain clean and lean. Okay, both words rhymed. :P

I will create a file `route.js` in each route folders which will export a route configuration. An example is given below,

![Code-File-2](https://thepracticaldev.s3.amazonaws.com/i/c3fuebpbvry8m1s0to9j.png)

I did the above for other routes and also created a route.js file in the `src/screens/App` directory to import all those routes as given below.

![Code-File-3](https://thepracticaldev.s3.amazonaws.com/i/k3o37vvpvcgye1d6hho4.png)

The next thing would be to make changes to our App component for incorporating these route config additions.

Just so that you know, I am referring to the react-router-dom [DOC](https://reacttraining.com/react-router/web/example/route-config) for doing all these changes required for the route-config to work.

Moving on, I am going to create a folder in the `src/shared` directory, which will hold those components that can be shared across all our applications. For now, I am going to add one component in it which will be used for rendering route components.

![Code-File-4](https://thepracticaldev.s3.amazonaws.com/i/70bvojiriwvd4kkt5mgx.png)

This component will receive an object containing the path to route and component to render. Let's export the above-shared component with an `index.js` file in `src/shared/components`

```js
export { default as RouteWithSubRoutes } from './RouteWithSubRoutes.js';

export default {};
```

Next thing, we will make changes in the App and Navbar component. Pasting the code here.

```react
import React from 'react';
import {
  BrowserRouter as Router,
  Switch,
} from 'react-router-dom';

import { RouteWithSubRoutes } from 'shared/components';

import './App.css';
import routes from '../route';
import Navbar from './Navbar';

function App() {
  return (
    <Router>
      <div className="App">
        <h1>App Component</h1>
        <Navbar />
        <Switch>
          {routes.map((route, i) => (
            <RouteWithSubRoutes key={i} {...route} />
          ))}
        </Switch>
      </div>
    </Router>
  );
}

export default App;
```

```react
import React from 'react';
import { Link } from 'react-router-dom';

import { routeConstants } from 'shared/constants';

const {
    LOGIN,
    DASHBOARD,
    LISTING,
    PROFILE,
} = routeConstants;

const navItems = [LOGIN, DASHBOARD, LISTING, PROFILE];

function Navbar() {
    return <ul className="App-nav-list">
        {
            navItems.map((navItem, i) => (
                <li key={i} className="App-nav-item">
                    <Link to={navItem.route}>{navItem.name}</Link>
                </li>
            ))
        }
    </ul>
}

export default Navbar;
```

So now the concerns are separated. We import route objects from individual Login, Dashboard, Listing, and Profile directories and form it into a single route array in `src/screens/App` directory. This way each screen is responsible for its existence. 

Now, if you have to change anything about any particular screen or add a new feature to a particular screen, the developer needs to just go to that folder, make changes in that sub-directory and that's it.

## Adding a new route

Let's go through the steps to add a new route. We will name it `About`.

First, we will add a new entry in `src/shared/constant/route.js`
```js
export default Object.freeze({
    LOGIN: {
        name: 'Login',
        route: '/'
    },
    DASHBOARD: {
        name: 'Dashboard',
        route: '/home'
    },
    LISTING: {
        name: 'Listing',
        route: '/list'
    },
    PROFILE: {
        name: 'Profile',
        route: '/me'
    },
    ABOUT: {
        name: 'About',
        route: '/about'
    }
});
```

Second, we will make changes to Navbar to add the newly added route. But wait a minute, I don't want to do that. Let's make some changes to the Navbar component so that we don't need to keep changing it next time we add a new route.

```react
import React from 'react';
import { Link } from 'react-router-dom';

import { routeConstants } from 'shared/constants';

const navItems = Object.values(routeConstants);

function Navbar() {
    return <ul className="App-nav-list">
        {
            navItems.map((navItem, i) => (
                <li key={i} className="App-nav-item">
                    <Link to={navItem.route}>{navItem.name}</Link>
                </li>
            ))
        }
    </ul>
}

export default Navbar;
```

Third, we will create a new folder named `About` under `src/screens/App/screens`. Also, quickly add a `components` folder, `index.js`, `route.js`, and add `About.js` file to the newly created `components` folder which will contain our route component.

`src/screens/App/screens/About/components/About.js`
```react
import React from 'react';

function About() {
  return <h1>About</h1>;
}

export default About;
```

`src/screens/App/screens/About/route.js`
```js
import { routeConstants } from 'shared/constants';
import About from "./";

export default {
    path: routeConstants.ABOUT.route,
    component: About
};
```

`src/screens/App/screens/About/index.js`
```js
import About from './components/About';

export default About;
```

Lastly, we will need to import and add About route in `src/screens/App/route.js`
```js
import LoginRoute from "./screens/Login/route";
import DashboardRoute from "./screens/Dashboard/route";
import ListingRoute from "./screens/Listing/route";
import ProfileRoute from "./screens/Profile/route";
import AboutRoute from "./screens/About/route";

export default [
    LoginRoute,
    DashboardRoute,
    ListingRoute,
    ProfileRoute,
    AboutRoute
];
```

And we are up with a new `About` route


## Sub routes

Let's imagine a scenario where you need to add new sub-routes as child routes to an already existing one. I will pick up the `Profile` route to do that. We will have an index route `Details` which shows Profile details and another route `Settings` to show a list of profile settings.

So `localhost:3000/me` will render `Details` and `localhost:3000/me/settings` will render `Settings`

First, create a screens folder in Profile sub-directory.

Second, we will add the following detail under PROFILE key in `src/shared/constants/route.js`
```js
    PROFILE: {
        name: 'Profile',
        route: '/me',
        subroutes: {
            SETTINGS: {
                name: 'Settings',
                route: '/me/settings'
            }
        }
    },
```

Third, we will create two new folders named `Details` and `Settings` under following path `src/screens/App/screens/Profile/screens`. Also, quickly add a `components` folder, `index.js`, `route.js`, and add the component file to the newly created `components` folder which will contain our route component. Make sure you perform this for both `Details` as well as `Settings` subroutes. Following snippets show what needs to be done.

File content - Details.js
```react
import React from 'react';

function Details() {
  return <h1>Details</h1>;
}

export default Details;
```

File content - Settings.js
```react
import React from 'react';

function Settings() {
  return <h1>Settings</h1>;
}

export default Settings;
```

`src/screens/App/screens/Profile/screens/Settings/route.js`
```js
import { routeConstants } from 'shared/constants';
import Settings from "./";

export default {
    path: routeConstants.PROFILE.subroutes.SETTINGS.route,
    component: Settings
};
```

`src/screens/App/screens/Profile/screens/Details/route.js`
```js
import { routeConstants } from 'shared/constants';
import Details from "./";

export default {
    exact: true,
    path: routeConstants.PROFILE.route,
    component: Details
};
```

`src/screens/App/screens/Profile/screens/Settings/index.js`
```js
import Settings from './components/Settings';

export default Settings;
```
Note: Do this with Details subroute too.

Next step would be to update our Profile route config

![Code-File-5](https://thepracticaldev.s3.amazonaws.com/i/zvm41665c58z2gqekbuu.png)

Lastly, we will need to add a navigation link to the Profile screen. For that, we will make changes in the Profile component.

`src/screens/App/screens/Profile/components/Profile.js`
```react
import React from 'react';
import { Switch, Link } from 'react-router-dom';
import { RouteWithSubRoutes } from 'shared/components';
import { routeConstants } from 'shared/constants';

const { PROFILE } = routeConstants;
const { SETTINGS } = PROFILE.subroutes;

function Profile({ routes }) {
  return <>
    <h2>Profile</h2>
    <ul className="App-nav-list">
      <li className="App-nav-item">
        <Link to={PROFILE.route}>Details</Link>
      </li>
      <li className="App-nav-item">
        <Link to={SETTINGS.route}>{SETTINGS.name}</Link>
      </li>
    </ul>
    <Switch>
      {routes.map((route, i) => (
        <RouteWithSubRoutes key={i} {...route} />
      ))}
    </Switch>
  </>;
}

export default Profile;
```

The File structure looks like this now.

![File-Structure-3](https://thepracticaldev.s3.amazonaws.com/i/ci2d21kzohj25e4m3b4g.png)

I know you guessed it right, it's a fractal structure which I implemented here. It is basically to repeat a structure when you zoom in to a particular folder. Of course, it is meant for the screens directory.


## Things that I left
1. Test files - I have not included writing tests in this post but make sure to keep it as close to the file that you are testing as possible.
2. CSS - I am a fan of a CSS-preprocessor but you can go ahead and use CSS-IN-JS or JSS which compliments this approach.
3. A pat on my back for writing this down. :P


## Benefits
1. Few to none git merge conflicts.
2. Separation of concerns
3. Easy to grasp at first glance
4. If you want to take out a particular route and move into its repository it is easy because we have built it to sustain itself.
5. Scaling is easy as steps to add new route or sub-routes is easy.


## Final App,

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/8abwms0sgdnyv66e6j5z.gif)


{% github surajhell88/react-spa-file-structure no-readme %}


## Conclusion

I know both posts were long enough to bore you down. But this is a very important time to invest in the file structure. Because as the product grows, we face more difficulties in managing our code files. We don't want to land on our wrong foot if a new feature compels us to redesign our whole app code.

Make sure each route contains all the code that it needs to run on, also keep an `index.js` file to export your main code file from a folder. Keep test files as closer to the code as possible. And most importantly do let me know if you have any questions in the comments section below.

## Thanks for reading