How to Start ReactJS Development Fast: 3 Solid Tools and Best Practices

Codica Team
Codica Journal
Published in
7 min readJan 16, 2020

The article was originally published on Codica Blog.

In this article, we want to discuss the most famous ready-made solutions that will help you start developing a React app without much effort.

Further, we will show you our best practices on the general React-Redux project structure.

It will allow you to pattern your project properly and keep the breakdown easy-to-read.

React development tools: CRA vs Next.js vs Gatsby

To start with, let’s discuss such open-source tools as create-react-app (CRA), Next.js, and Gatsby. With their help, you can start developing your ReactJS project without wasting time on the setup process, providing a basic structure template and minimal functionality. This way, you will be able to launch fast and smoothly. Further, of course, you can change your application structure configuration if necessary.

Now let’s take an overview and compare these tools, discussing their pros and cons, specific properties, and main differences.

CRA

Create-react-app (CRA) is a tool designed by Facebook. This solution provides you with a quick start when developing ReactJS applications. It allows you to avoid a time-consuming configuration process by creating a maintainable and easy-to-use React App scaffold. The tool installs all the default dependencies, packages, and generates a default directory structure.

To install the tool and start developing a React app, run the following command lines:

npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start

Next.js

To put it shortly, Next.js is a lightweight framework that allows you to create server-rendered and static React.js apps easily. You will need a server to run the project and it generates HTML on runtime. It means that each time a new request comes in, it produces a new HTML page from the server.

Take a look at the Next.js workflow in detail:

Here are the core features of the framework:

  • Server-Side Rendering;
  • Static files creation;
  • Automatic code splitting;
  • Prefetching;
  • Hot Module Replacement and Error Reporting.

To install the tool and start developing a React app with Next.js, run the following command line:

npm install — save next react react-dom

Then add scripts to your package.json:

{
“scripts”: {
“dev”: “next”,
“build”: “next build”,
“start”: “next start”
}
}

Finally, run the following command:

npm run dev

Gatsby

To understand properly, we should repeat that create-react-app provides Client-Side Rendering, Next.js provides Server-Side Rendering, but Gatsby is an open-source framework that refers to blazing-fast Static Site Generator.

If you are new at static site generation, let’s discuss how it works. Thus, the static side generators are those which produce HTML code during the “build” stage. Actually, it may be performed by fetching data from some APIs, markdown files, etc.

You can easily start developing a ReactJS app with Gatsby in the following way:

npm install -g gatsby-cli
gatsby new gatsby-site
cd gatsby-site
gatsby develop

CRA vs. Next.js vs Gatsby: a brief comparison

To sum up all the aspects of the discussed open-source solutions, let’s take a look at a brief comparison table.

Best practices on React-Redux project structure

In this section, we want to share our base recommendations on how to structure your React-Redux project files and code so that your application stays maintainable as it scales, based on our experience. The guideline is based on improving the create-react-app setup.

Initially, we decided to separate React and Redux into different folders. Thus, it simplifies the process of applying any changes or adding a new feature. Redux-specific code (reducers, actions, action types) is split by feature-first pattern Re-Ducks. Here is an example of a project structure we use in practice:

src/
├── state => redux specific code (Re-Ducks)
| ├── ducks/
| | ├── duck/
| | | ├── types.js
| | | ├── actions.js
| | | ├── selectors.js
| | | ├── reducers.js
| | | ├── tests.js
| | | ├── index.js
| utilities/ => global constants and helper functions
| views/
| ├── routes/ => base router
| ├── components/ => feature-first components
| ├── pages/ => layouts, related to routes
| ├── styled/ => StyledComponents
| └── UI/ => reusable components

We prefer to create the React components first and then the corresponding Redux code. It allows us to have a general understanding of the data requirements.

The /ducks directory has a fixed pattern. We use a modified version of the ducks pattern to organize our Redux code:

ducks/
├── duck/
| ├── actions.js
| ├── reducers.js
| ├── types.js
| ├── utils.js
| ├── selectors.js
| └── index.js
└── index.js

Now, let’s discuss each /duck folder file to understand why is it important and what it stands for.

Project structure files

types.js

This file contains string literals for our action types. It provides an easy reference to the actions available. These strings are exported as an object literal which can then be imported into your reducers and action creators instead of hard-coding them. Although maintaining a separate file that contains the action types is optional, it is highly recommended in organizing the structure of your project files.

// types.js
export const SOME_YOUR_TYPE = “SOME_YOUR_TYPE”;

actions.js

In this file, we define all the actions. Actually, some developers tend to separate async actions and action creators, but we don’t think it is pretty crucial.

// actions.js
import types from ‘./types.js’;

// action creator
const someAction = payload => ({
type: types.SOME_YOUR_TYPE,
payload
});

Actually, we use redux middlewares such as redux-thunk or redux-promise-middleware for dispatching async actions.

reducer.js

The reducer is required to update the state. We create a single reducer for each action using createReducer. We use this command to create reducers, not the basic switch-case template. The matter is it’s very useful, for example, if you need to scope out part of a reducer to use variables with the same name in several case statements.

// reducer.js
const someReducer = createReducer(initialState)({
[types.YOUR_ACTION_TYPE]: (state, action) => {
return {
…state,
some_prop: action.payload
};
},

[types.SOME_ANOTHER_TYPE]: (state, { payload: { data } }) => ({
…state,
data,
loading: false
}),

[types.MAY_BE_YOU_WANT_RESET]: (state, action) => ({
…initialState
})
});

selectors.js

In Redux, a selector is a piece of logic that receives a certain piece of state from the store. Additionally, a selector can compute data from a given state, allowing the store to hold only basic raw data. Selectors are usually used as a part of the binding between the store and the container components.

We use the Reselect library to create selectors. This library is not the only way or the requirement to create selectors. However, it gives several advantages in terms of developer experience and performance:

  • Selectors created through a createSelector function are memoized. It means that the function remembers the arguments passed-in the last time it was invoked. Thus, it doesn’t recalculate the result if the arguments are the same.
  • Selectors can be composed/chained together easily. This way, each selector stays small and focused on one task.

Here is a simple filteredTodos selector example to demonstrate how it works:

// selector.js

import { createSelector } from ‘reselect’;
const todoSelector = state => state.todo.todos;
const searchTermSelector = state => state.todo.searchTerm;

export const filteredTodos = createSelector(
[todoSelector, searchTermSelector],
(todos, searchTerm) => {
return todos.filter(todo => todo.title.match(new RegExp(searchTerm, ‘i’)));
}
);

With the help of this library, we can use the filteredTodos selectors to get all the todos if there’s no searchTerm set in the state, or a filtered list otherwise.

Also, we can get all the todos in a flat shape in conjunction with normalized data:

import { denormalize } from ‘normalizer’;

import { todo } from ‘../../schemas’;

const getById = state => state.todo.byId;

const getAllIds = state => state.todo.all;

export const makeAllTodos = () =>
createSelector(
[getAllIds, getById],
(all, todos) =>
denormalize(all, [todo], { todos}),
);

index.js

Here, we re-export all our actions, selectors and our reducer as a default export.

// index.js
export * from ‘./actions’;
export * from ‘./selectors’;

export { default } from ‘./reducer’;

Finally, our duck folder is ready!

This is the way we organize our React app structure to make the application maintainable when it scales.

Final words

If you want to skip the time-consuming setup process and start quickly with the development of your React application, use such extensive open-source tools as CRA, Next.js, and Gatsby. These solutions were designed to help you install all the default dependencies and packages automatically, structuring your project in the right way.

At Codica, we have created our Best Practices on React-Redux project development that can help you understand your application architecture and build a well-structured code. There you can find sample code snippets and more detailed explanations.

Want to know more about front-end development with React? We recommend you to take a look at our next blog article:

Originally published on Codica Blog.

--

--

Codica Team
Codica Journal

Software development consultancy. We are passionate about innovations and create great online marketplaces with Ruby on Rails, React, Vue and Angular.