Redux 4 + TypeScript: A type-safe approach

An updated version of my type-safe guide to Redux, now compatible with Redux 4+ and TypeScript 3.0+.

Resi Respati
Jul 6, 2018 · 11 min read

This post is also published on my personal website.

Even when the JavaScript community is slowly growing mixed opinions about it, I couldn’t help but continue using Redux. Its patterns on immutable state management has become all too familiar to us, and is especially useful when building large apps. Its TypeScript support is exceptional too, with much-needed improvements to its type declarations arriving in Redux 4.

I wrote a guide on it a few months ago, and it has received some amount of attention. The feedback has helped me improve beyond what I normally would’ve done, and I couldn’t thank you enough for that.

In spirit of that, I finally took the time to update said guide based on the feedbacks I’ve received, making everything up to date with the latest version of React, Redux, and TypeScript, as well as introducing some neat new tricks.

Note that the following guide is tested on:

  • react@^16.4.0
  • redux@^4.0.0
  • typescript@^3.0.0

What we’re building

To demonstrate this post, we’re going to build a simple app. We’re going to create a website which pulls data from the OpenDota API, and display information about certain heroes and professional teams. This will also demonstrate how to structure your stores for each feature/module in a Redux-enabled app.

TL;DR

If you want to jump straight to the examples, I’ve also published a sample project in GitHub, based on the feedback from my previous post. Click here to go there.


Updates

  • 2018–12–08: Updated Dispatch to be imported from redux instead of react-redux. The guide is also now tested to work on TypeScript ^3.0.0. (Thanks cancerberoSgx!)
  • 2019–01–05: Changed const enums to enums due to Babel not supporting it. (Thanks Kyle Gillen!)
  • 2019–03–09: The latest version of react-redux broke the typings for the"children-props-as-redux-container" approach I mentioned in the previous version of this post. I would suggest against using this pattern nowadays, but if you still want to use it, I've upgraded the corresponding section in this article to have the same pattern, making use of the newly-introduced ReactReduxContext.
  • 2019–09–22: The above pattern breaks on a Create React App setup. The entire LayoutContainer is now rewritten using built-in react-redux hooks.

Directory structure

I’ll level with you, one of the hardest steps in getting started with working on React + Redux for me is figuring out how to structure your project. There’s really no de facto way to do this, but it’s still important to get this right so to not cause further distractions down the road. Here’s how I normally do it.

Use a dedicated store/ directory

A lot of the guides/projects out there structure their store separately inside a root actions/ and reducers/ directory, to mimic the patterns in Redux architecture.

(Note that the following directory trees assume that your code is placed inside a src/ directory.)

However, I personally find this to be distracting. When your codebase grows larger, you would end up scattering code which shares the same context across a great length of the directory tree, which wouldn’t be intuitive for newcomers who wanted to take a quick glance at your code. Therefore, roughly following the Redux pattern in this case is less advantageous in terms of code readability.

So I decided to dedicate a store/ directory for all my Redux actions/reducers. This method is mostly borrowed from this guide made by Tal Kol of Wix, with a few adjustments.

Group stores by context

As an extension to the guides above, the state tree should be structured by context.

Combine reducers inside store/index.ts

Include an index.ts file at the root of the store/ directory. We'll use this to declare the top-level application state object type, as well as exporting our combined reducers.

Store types

Include a types.ts file inside each store module. This is where we hold our state types, as well as any other types related to this Redux store module.

Typing actions

Now that we have everything scaffolded, time to set up our actions!

Writing typesafe actions with typesafe-actions

Piotrek Witek created the typesafe-actions library, which provides useful helper functions to create type-safe Redux actions. We'll use this to write our Redux actions.

Typing reducers

Typing reducers is a lot more straightforward with Redux 4.

Handling actions asynchronously with redux-saga

If your action dispatcher involves making numerous asynchronous tasks, it’s better to include a library which handles side-effects on Redux. The two commonly-used libraries for this are redux-thunk and redux-saga. We’re going to use redux-saga due to its cleaner API, which makes use of generator functions.

To include them in our root store, we add a rootSaga() generator function which collects all of our store sagas.

Initialising Redux store

Initialising the Redux store should be done inside a configureStore()function. Inside this function, we bootstrap the required middlewares (including redux-saga) and combine them with our reducers.


Connecting with React

Now let’s hook everything up with React.

Container components

Update: The latest version of react-redux broke the typings for the"children-props-as-redux-container" approach I mentioned in the previous version of this post. I would suggest against using this pattern nowadays, but if you still want to use it, here's a way to upgrade, using the brand-new useDispatch and useSelector hooks:

This way, we can use the Redux store linking from any component!

Page components

When connecting pure React components, it’s a good idea to connect them at the page level. As a reminder, when mapping states/action dispatcher to a component, we need to combine the state/action dispatcher prop types of the store we’re mapping to our component prop types as well.

Using react-redux's connect()

The react-redux connect() function is what connects our React component to the redux store.

Additional helper type

We can also add a helper type for our connected Redux components.

So now on any Redux-connected component, we can extend its props interface with the interface above.


Sample code

Hope you’ve found this guide useful! Based on your feedback as well, I’ve also published a sample project following the guides above on GitHub. Click here to go there.

References

To learn more about React, Redux, and TypeScript, the following guides are a good read:

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store