Introduction
Before we dive in, let’s revisit how Typescript and React works together. React is a JavaScript library for building interactive user interfaces, while Typescript is a “typed super-set of JavaScript that compiles to plain JavaScript.” By using them together, we will essentially build our user interfaces using a typed version of JavaScript.
The main reason you might use them together would be to get the benefits of a statically typed language for the user interfaces. Which means more safety and fewer bugs shipping to the front end.
Glossary
- React – A JavaScript library for building user interfaces.
- create-react-app (CRA) – A tool from Facebook for building modern React applications.
- Typescript– A typed super-set of JavaScript that compiles to plain JavaScript
- Redux-Toolkit – The official, opinionated, batteries-included tool set for efficient Redux development.
- Redux-Injectors – Dynamically load Redux reducers and Redux-saga sagas as needed, instead of loading them all upfront.
- 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.
Setup CRA with Typescript
Like we did on my previous blog, we’ll first create a Counter app using React, Typescript, Redux-Toolkit, and Redux-Saga. This will act as the boilerplate of our custom template. When configuring a custom Create React App and Typescript template, I find it easiest to bootstrap it with CRA itself.
To start a new Create React App project with Typescript, you can run:
npx create-react-app cra-template-my-app --template typescript
CRA will do its thing (substitute my-app 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-my-app
├── public
├── src
│ ├── counter
│ ├ ├── index.ts
│ ├── sagas
│ ├ ├── index.tsx
│ ├── App.css
│ ├── App.tsx
│ ├── App.test.tsx
│ ├── Counter.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── react-app-env.d.ts
│ ├── rootReducer.ts
│ ├── serviceWorker.ts
│ ├── setupTests.ts
│ ├── store.ts
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md
To add Typescript to a Create React App project, first install it:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
Next, rename any file to be a TypeScript file ( e.g. src/index.js
to src/index.tsx
) and restart your development server!
Type errors will show up in the same console as the build one. You’ll have to fix these type errors before you continue development or build your project.
This will get you the bare minimum to start writing React with TypeScript. A few noticeable differences are:
- the
.tsx
file extension - the
tsconfig.json
- the
react-app-env.d.ts
The tsx
is for “Typescript JSX”. The tsconfig.json
is the Typescript configuration file, which has some defaults set. The react-app-env.d.ts
references the types of react-scripts
, and helps with things like allowing for SVG imports.
For advanced configuration, see here.
Step 1 – Redux Saga and Store Setup
For this tutorial, we will be using Redux which is an external state management library system. It’s popular and therefore has a lot of support. We will also be using Redux-Toolkit a toolset of batteries for efficient Redux development.
We will install the following:
npm install redux react-redux redux-injectors
npm install --save-dev redux-devtools-extension
Redux-Injector will dynamically load redux reducers and redux-sagas as needed, instead of loading them all upfront. This has some nice benefits, such as avoiding having to manage a big global list of reducers and sagas. It also allows more effective use of code-splitting.
Store Setup
All the logic related to configuring the store – including importing reducers, middleware, and enhancers – is handled in the src/store.ts
file.
To achieve this, configureAppStore
function looks like this:
createSlice
is a function that accepts an initial state, an object full of reducer functions, and a “slice name”, and automatically generates action creators and action types that correspond to the reducers and state. The app reducer functions will be in the src/counter/index.ts
file.
createReducer
is a utility that simplifies creating Redux reducer functions, by defining them as lookup tables of functions to handle each action type. It also allows you to drastically simplify immutable update logic, by writing “mutative” code inside your reducers.
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.tsx
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.tsx
file should be as follows:
Step 2 – Bind with React Redux
In the /index.tsx
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.tsx
file. Here we are getting the values from the main component(App.tsx) as props.
Step 4 – Setup App.tsx
Inside the /App.tsx
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 Typescript template
In this second part, we will be covering how to convert the Typescript Redux-Saga counter app to a custom template.
A template must have the following structure, so update our app to reflect this:
├── cra-template-my-app
├── template
│ ├── public
│ ├── src
│ ├ ├── counter
│ ├ ├ ├── index.ts
│ ├ ├── sagas
│ ├ ├ ├── index.tsx
│ ├ ├── App.css
│ ├ ├── App.tsx
│ ├ ├── App.test.tsx
│ ├ ├── Counter.tsx
│ ├ ├── index.css
│ ├ ├── index.tsx
│ ├ ├── logo.svg
│ ├ ├── react-app-env.d.ts
│ ├ ├── rootReducer.ts
│ ├ ├── serviceWorker.ts
│ ├ ├── setupTests.ts
│ ├ ├── store.ts
│ ├── gitignore
│ ├── README.md
│ ├── tsconfig.json
├── package.json
├── template.json
└── 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 file system. 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 my-app --template file:.
in your template root folder.
npx create-react-app my-app --template file:../path/to/your/template/cra-template-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
.
Learning Tools
There is a lot that we can gain from Typescript when using it to type check. Find below useful links to learn more about Typescript, React, Redux-Toolkit, Redux Injectors, Create React App, and Redux Sagas.
- Typescript and React – https://www.typescriptlang.org/docs/handbook/react.html
- Redux Toolkit Docs – https://redux-toolkit.js.org/
- Redux Injectors – https://github.com/react-boilerplate/redux-injectors
- Smarter Redux with Redux Toolkit – https://blog.logrocket.com/smarter-redux-with-redux-toolkit/
Learning Strategy
To get the most out of this article and the GitHub project, you will need to have basic knowledge of how Typescript works as a super-set of JavaScript in a React app. You also need to understand how redux-saga a Redux middle ware 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 Typescript and React.
Reflective Analysis.
As the code base of a React app grows, it can become too difficult to read, share, or maintain. Navigating these code bases can be tedious, and refactoring code is risky. Typescript helps you refactor code and prevent typos, making it far easier to maintain and update without changing any of its behaviors. Also one of the most loved features of Typescript is its ability to highlight errors as soon as they crop up. Typescript shows you errors before you ever run the code, saving hours of time fixing bugs or mistakes early on.
Conclusion
This was a very basic intro to Typescript majorly focusing on how to setup Typescript using CRA(Create React App) which should enable anyone to start with Typescript.
You can always refer to the GitHub repository to clone the project we have created.
Please share your thoughts about this article in the comment section, I’d be happy to talk! Thanks for reading, learning and sharing.
More on React and Redux, checkout out this: How to create a Custom Redux-Saga Template with CRA
Conclusion
Basically the Project is all about Custom Typescript and Redux-Saga Template with CRA where when both are combined, it would lead to a more efficient user-Interface that has better security features and fewer bugs for the front-end. The blog has clearly defined and explained each and every aspect that is necessary to get started within this project and has used simple terms. The code is also working as expected and I have no corrections.
Future Directions of The Project
I have but a few suggestions where you stated that Typescript is a “typed super-set of JavaScript that compiles to plain JavaScript.”. I would suggest if you could enable that this Typescript could compile with more languages such as Python so that we could have endless possibilities
As stated ” Typescript is a “typed super-set of JavaScript that compiles to plain JavaScript.”” – This means that Typescript only works with JavaScript ans its frameworks and libraries.
On the other hand, Python unlike JavaScript is strong typed so the need for something like Typescript is lessened. Instead you can use type annotations and either the mypy linter or your ides linter to enforce the typing.
In effect, this gives you all the same benefits as Typescript without so much overhead.