React, Redux and TypeScript...how do they all come together?

2024-01-06

Exploring the integration of React, Redux, and TypeScript

This week I worked on a small project which helped deepen my understanding of Redux, specifically how it works together with React and TypeScript. The application I built was pretty simple, it basically uses the public npm registry to query for packages based on a query parameter within the url ("https://registry.npmjs.org/-/v1/search?text=react"). The application takes the response from the API and lists out the packages which match the search term provided. This project felt like a pretty straight forward, yet common use case for a lot of organizations. In my experience developing web applications one of the most common use cases involves calling out to an API, receiving a response and then processing the data within that response. Some of the biggest pain points are around maintaining a consistent version of that state and wiring up all of your components to that state tree in a simple and consistent way. What I was mainly trying to get out of this project was a better understanding of Redux overall, along with how it works together with TypeScript, and honestly I think that this is a great introductory project for anyone trying to understand how React, TypeScript and Redux work together.

Why not just use React Context API? The React Context API is a common tool that many devs reach for when handling state management use cases with React. And it's definitely a great tool readily available right in the React framework. So this would definitely be a great tool to use for a small to medium size project, and especially for someone just starting out and wanting to minimize the learning curve with using a state manager (Redux definitely has a slightly steeper learning curve than the React Context API). React Context API is also specific to the React framework, unlike Redux which can be used with any framework which gives developers a lot more flexibility (not saying that most devs don't reach for React as a front-end, but hey you never know what the future will bring...). This blog post focuses on the implementation of Redux with React and TypeScript and how TypeScript compliments this stack of tools. In the future I'll look at doing a similar blog post specifically on the React Context API.

The first thing that was important for me was getting a full refresher on Redux. I've used this library in the past, specifically for a Salesforce implementation that I worked on which involved a number of custom web components built using the Lightning Web Components framework, however it has been a while so there were quite a few cobwebs to be blown off. There have also been considerable updates to the Redux library over the years which made it feel like an entirely new library. And of course the fact that I was trying to understand how TypeScript fit into the picture forced me to look at Redux from a different frame of mind. So it's safe to say I went into this project as a total novice, picking up the concepts for the first time and viewing them from a different perspective.

So what's Redux anyway? At it's core, Redux is a state management tool for JavaScript. It's a very popular tool to use for state management because of the fact that it uses a unidirectional data flow model. This makes it really predictable when implemented correctly. Although it's not required when building a React application, it's very commonly used together with React because of what I mentioned before.

What are Redux Actions? Actions are bits of JavaScript code which represent intentions to change the state of your application. Each action has a 'type' (not to be mistaken with TypeScript types) which represent the type of action. These types are defined by the application you're working on. For my project, I had three action types, 'search_repositories', 'search_repositories_success', and 'search_repositories_error'. Each of these were specified on a separate actions, so for example the 'search_repositories' type belonged to the 'SearchRepositoriesAction' action. These actions each represented an intention to change the state of the application.

How do Redux Actions work together with TypeScript? When implementing actions with TypeScript, it's important to define specific interfaces for these actions. Your inventory of actions can quickly get complex when implementing Redux, so having the TypeScript compiler checking that the implementation of the actions doesn't violate the pre-defined data types is a huge benefit.

How do Redux Reducers work? Reducers are responsible for manipulating the state of your application. A reducer takes in two parameters, the current state of the application and the action you'd like to execute on that state. For anyone that's made pasta before, you can think of the reducer as the pasta roller and the state is the pasta dough. Your initial state is usually a thick piece of dough that needs to be flattened out. The actions mentioned above can represent the thickness setting of your pasta roller. Depending on what thickness you're trying to get the pasta to, you'll specific the thickness level (the action) and run your dough through the roller (pass in your initial state and the action). Reducers are defined using JavaScript functions.

What are the benefits of using Reducers with TypeScript? When writing reducers with TypeScript, it's pretty much the same as JavaScript, with one clear distinction, which is type specifications. When passing in the state and action to the reducer, it's important to specify the type of each of these properties. This is where it can be extremely beneficial to have type checking in place. The project I worked on only has a few actions so it wasn't difficult to keep all the different types and schemas in my head, but this can easily get really complicated when working on a large project with other developers.

What are Redux Action Creators? Action creators are JavaScript functions that create actions (based on the actions you defined earlier). I liken action creators to a sort of 'binding' between the action types and what you're actually trying to do when your reducer is called. Sticking with the pasta analogy, if the actions are the settings on the pasta maker, the action creators would be the gears which spin and link the thickness setting (action) to the resulting thickness of your dough after you run it through the roller (reducer).

How do Action Creators work with TypeScript? The biggest benefit I noticed here was the TypeScript type checker and auto-complete. Especially with redux, you're working with multiple components which reference other components in order to maintain the overall state. Having a squiggly line show up when something isn't quite right with a helpful hint saved me lots of headaches down the road.

Selectors: Selectors are a redux functionality that enables you to select a specific part of our state tree. In the project I worked on, I used the selector to deconstruct the data, error and loading pieces of my state which were used within my React component.

What's so special about using Redux Selectors with TypeScript? Ok, now here's where I hit a little friction. This piece was definitely not very smooth, it almost felt like redux and TypeScript didn't want to play well together. I won't get into all of the detailed points on why this was so painful (I think that would be a separate blog post entirely) but the TL;DR is this; you'll have to refer to the redux documentation and implement the steps needed so that your selectors are converted to 'typesSelectors'. Once you have that piece in place (however wonky it might look and feel), the selectors work seamlessly!

What role does a Store play in Redux? This is the foundation of redux, the store which represents your application's state. Creating the store is simple, just use the standard 'createStore' function from the redux library and you're off to the races. The store takes in a few arguments, your reducers, an empty object (or your initial state) and a middleware (more on that in the next and final point).

What's the benefits of using a Store with TypeScript? The clearest benefit of using TypeScript is the ability to strictly define and enforce your store. Considering how important the state is for your application to function properly, it's hard not to see the benefits of having a store which is predictable (thanks TypeScript!).

What's this Middleware thing and why do I need it? The middleware serves as almost an orchestrator which handles logging, crash reporting and completion of asynchronous tasks among other things. I won't get too into the weeds on the middleware (I used thunk in the project I worked on, but there are a number of options available in the Redux ecosystem) as this topic deserves it's own stand-along blog post. The most important thing to understand here is that as part of redux, there are actions which are dispatched in order to manipulate the state of your application and those actions will return pieces of state which you'll want to reference within your application. That whole song and dance is enabled by the middleware.

What benefits does TypeScript bring when implmenting a Middleware? TypeScrip's type checker and auto-completion really shines with the middleware. Specifically by providing the developer with the information they need when trying to understand the shape of the actions and the state transitions they handle.

In summary, when using Redux together with TypeScript, the first thing I noticed was that there is a lot of additional code being written (some a bit wonky if I'm being honest). However, once the application started coming together I almost immediately realized the benefits that TypeScript added. I was able to quickly identify errors and the autocompletions provided by the TypeScript compiler was a breath of fresh air!

I would like to thank Stephen Grider for putting together an amazing course on Udemy which served as my guide through the project I worked on. I highly recommend this course if you haven't already taken it. Stephen does a great job talking through all the nuances of React, TypeScript, Redux and more. Not to mention, the course is actively updated when things change in the framework. Can't tell you how many times I've tried to pick up a framework and when I dive into a course that walks through a project, everything is out of date and there are exceptions all over the place. thanks Stephen!