Introduction

Custom React Templates enable individuals or teams to select a template to create their project from, while still retaining all of the features of Create React App. In this article, we’ll build a Custom React Redux Saga Template with create-react-app. A tool from Facebook for building modern React applications without having to worry about configuring the development environment and optimizes your app for production. After creating the template we will also publish it to npm.

Project GitHub repository

Glossary

  • React – A JavaScript library for building user interfaces.
  • create-react-app(CRA) –  A tool from Facebook for building modern React applications.
  • Redux – A Predictable State Container for JavaScript Apps.
  • React-Redux – The official React binding for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update data.
  • Redux-Saga – A JavaScript library that aims to make application side effects easier to manage, more efficient to execute, easy to test, and better at handling failures.

NOTE: We will not be using the Redux Toolkit package, it abstracts the React and Redux setup process. My main goal here is to show you how to create a custom React template with Redux and sagas, step by step. But don’t worry, in my next article I will be showing you how to use the said package alongside Typescript.

Setup

First, we’ll create a Counter app using React, Redux, and Redux-Saga that will act as the boilerplate of our custom template. When writing a custom Create React App template I find it easiest to bootstrap it with CRA itself.

npx create-react-app cra-template-react-redux-saga

CRA will do it’s thing (substitute react-redux-saga with whatever you want to name your project). You’ll notice that Custom Templates are always named in the format cra-template-[template-name], however, you only need to provide the [template-name] to the creation command. Then cd into the directory to layout the app directory structure.

Here’s what it should look like:

├── cra-template-react-redux-saga
       ├── public
       ├── src
       │   ├── actions
       │   ├   ├── index.js
       │   ├   ├── types.js
       │   ├── reducers
       │   ├   ├── index.js
       │   ├── sagas
       │   ├   ├── index.js
       │   ├── App.css
       │   ├── App.js
       │   ├── App.test.js
       │   ├── Counter.js
       │   ├── index.css
       │   ├── index.js
       │   ├── logo.svg
       │   ├── serviceWorker.js
       │   ├── setupTests.js
       │   ├── store.js
       ├── .gitignore
       ├── package.json
       └── README.md

Step 1 – Redux Saga and Store Setup

React supports several external state management libraries/systems, including X-State and Redux. For this tutorial, we will be using Redux. It’s popular and therefore has a lot of support. It also has a toolset of batteries for efficient Redux development.

We will install the following:

npm install redux react-redux

npm install --save-dev redux-devtools-extension

Another common feature that we will add to our app is redux-devtools-extension integration.

An extension that allows you to inspect and replay actions, explore your state at different times, dispatch actions directly to the store, and much more. Click here to read more about the extension.

Store Setup

All the logic related to configuring the store – including importing reducers, middleware, and enhancers – is handled in the src/store.js file.

To achieve this, configureStore function looks like this:

Store the state of our app in an object tree inside a single store. The only way to change the state tree is to emit an action, an object describing what happened. To specify how the actions transform the state tree, you write pure reducers in the src/reducers/index.js file.

Sagas

Sagas are implemented as Generator Functions that yield objects to the redux-saga middleware. The middleware interprets the yielded objects as instruction. When a Promise is yielded to the middleware, the middleware will suspend the Saga until the Promise completes it. The middleware will suspend the Saga until the yielded Promise completes. In the code below found in src/sagas/index file, the incrementAsync Saga is suspended until the Promise returned by delay resolves, which will happen after 1 second.

Once the Promise is resolved, the middleware will resume the Saga, executing code until the next yield. The next statement is another yielded object: the result of calling put({type: 'INCREMENT'}), which instructs the middleware to dispatch an INCREMENT action.

put is one example of what we call an Effect. Effects are plain JavaScript objects which contain instructions to be fulfilled by the middleware. When a middleware retrieves an Effect yielded by a Saga, the Saga is paused until the Effect is fulfilled.

So to summarize, the incrementAsync Saga sleeps for 1 second via the call to delay(1000), then dispatches an INCREMENT action.

Next, we created another Saga watchIncrementAsync. We use takeEvery, a helper function provided by redux-saga, to listen for dispatched INCREMENT_ASYNC actions and run incrementAsync each time.

Now we have two Sagas, and we need to start them both at once. To do that, we’ll add a rootSaga that is responsible for starting our other Sagas. The src/sagas/index.js file should be as follows:

Step 2 – Bind with React Redux

In the /index.js file, we pass the Redux store object to the react-redux Provider component, which is rendered at the top of our component tree.

This ensures that any time we connect to Redux, the store is available to our components.

Step 3 – Create a Counter Component

We need three buttons for the view. An incrementAsync(delay +) button, increment(+) button and a decrement button(-). As we are on React, we will create a Counter component that will contain our button components inside the /Counter.js file. Here we are getting the values from the main component(App.js) as props.

Step 4 – Setup App.js

Inside the /App.js file import Counter component we created earlier and update the file to look like the one below.

Update the App.css file to reflect the following:

Now run npm start to test the Counter App by clicking on the increment, increment async and decrement buttons.

Step 5 – Building the template

In this second part, we will be covering how to convert the React Redux counter app to a custom template.

A template must have the following structure, so update our app to reflect this:

├── cra-template-react-redux-saga
       ├── template
       │   ├── public
       │   ├── src
       │   ├   ├── actions
       │   ├   ├   ├── index.js
       │   ├   ├   ├── types.js
       │   ├   ├── reducers
       │   ├   ├   ├── index.js
       │   ├   ├── sagas
       │   ├   ├   ├── index.js
       │   ├   ├── App.css
       │   ├   ├── App.js
       │   ├   ├── App.test.js
       │   ├   ├── Counter.js
       │   ├   ├── index.css
       │   ├   ├── index.js
       │   ├   ├── logo.svg
       │   ├   ├── serviceWorker.js
       │   ├   ├── setupTests.js
       │   ├   ├── store.js
       │   ├── gitignore
       │   ├── README.md
       ├── package.json
       ├── template.js
       └── README.md

The template folder

The folder will be copied to the user’s app directory when Create React App installs. During this process, the file gitignore is renamed to .gitignore.

You can add whatever files you want in here, but you must have at least the files specified above.

The template.json file

This is the configuration file for your template. As this is a new feature, more options will be added over time. For now, only a package key is supported.

The package key lets you provide any keys/values that you want to be added to the new project’s package.json, such as dependencies (only dependencies are supported for now) and any custom scripts that your template relies on.

Below are the contents of our template.json file:

Step 6 – Testing a template

Create React App can use a local template on your filesystem. This is useful for development, or if you don’t want to publish. To test a template locally, pass the file path to the directory of your template root using the file: prefix.

Use npx create-react-app your-app --template file:. in your template root folder.

npx create-react-app my-app --template file:../path/to/your/template/cra-template-react-redux-saga

Step 7 – Publishing a template on npm

Publishing to npm is a topic of its own. To keep the scope small: After registering at npm and authorizing on the CLI

npm login

Run the following command to publish the project as an npm package:
npm publish --access public

Wow, congratulations, you should now have created and published a Create React App custom template. Bootstrap a new React application with npx create-react-app your-app --template your-custom-template.

The published npm package

Learning Tools

There is a lot that we can gain from Redux Saga when using it to handle asynchronous operations in the React app. To learn more, find below useful links to learn more about React, Redux, Create React App, and Redux Sagas.

Learning Strategy

To get the most out of this article and the GitHub project, you will need to have basic knowledge of how Redux works as a state management tool in a React Redux app. You also need to understand how redux saga which is a Redux middleware handles React applications side effects. Armed with that knowledge, I believe you can be able to apply what you have learned here when working with client-facing applications built with React and Redux.

Reflective Analysis

The benefits of using Redux Saga as an alternative approach to the organization of side effects cannot be overly emphasized. Instead of dispatching functions processed by Redux Thunk you create saga and write all the logic of event stream processing. Redux Thunk is the default approach of handling side effects in React application. Unlike thunks that are carried out when you dispatch them, sagas run in the background right after the app is launched. Sagas observe all actions that the store dispatches and decide what to do.
Some of the advantages of using Sagas are simplicity in organizing difficult side effects sequences, declarative style, and simplicity of testing.

Conclusion

This example is pretty simple but it covers some common patterns in React.

In a more complex React Redux application we would start using lifecycle methods but this basic example actually covers most of the core React Redux features. In my next article I will be showing you how to use Typescript which is a typed super set of JavaScript.

You can always refer to the GitHub repository to clone the project we have created.

More on Redux, checkout out this: How to test Asynchronous Redux actions using jest.