Typescript React Redux tips that make developing a breeze 🍃
Just wanted to share a few tips that I’ve picked up / developed / adapted through my experience working in a Typescript React Redux stack that have really made writing correct code really (br-)easy 🌬.
Add a generic type for Actions
The Action type that ships with Redux does not immediately provide support for types on action payloads, which is an extremely common use case.
This is a useful starting point to provide a common interface for all actions in the codebase and adding type safety to the action payloads.
Organize Redux code by feature, not by type of function
There’s this funny human tendency to get a kick out of putting like things together: putting all the reducers together in one file, action types in another, and action creators in still another— it all just feels so satisfying.
However, this type of behavior ends up biting us in Redux projects, where we’re typically working on a single feature involving each type of Redux thing. We end up having five different files all open at once, each composed 95% of code that is completely unrelated to the feature that’s being worked on. Not ideal.
The way we organize our code should be optimized for how we develop that code. Let’s use the addTodo
action from the ol’ todos app as an example of how we can do that.
All the related code is together. I don’t need to spend extra time importing and exporting variables to each other — they’re all in the one file already. And everything mutates the todos state is visible at once.
Note that there is room in this file for more than one action type. The decision and power is left in the developer’s hands on whether another action (say, toggleTodo
) is related enough to be put into this file or separate enough to be deserving of a new file.
Take advantage of discriminated union types
Hey, speaking of putting more than one action type in a file, let’s check out how we can use our fancy new Typescript Action
type to take advantage of ✨discriminated union types✨. (Full code included for clarity, but scroll down to the reducer to see the discriminated union types in action!)
Discriminated union types allows us to get strong Action
types in our reducers, which along with strong State
typing makes writing correct reducers super easy.
Consistently organize Redux exports
Every Redux file should export the exact same items:State
, reducer
, andactions
. Keeping a consistent export shape makes reusing and combining Redux files a super simple.
As an example, I’ve added a “rewards” feature to keep track of rewards for completing our todos 🎂. Here’s what the resulting combination of the rewards and todos features would look like.
This is what a typical “index” Redux file looks like, which simply has the function of combining Redux features. Note that the todos and rewards features each have a consistent public API which is completely decoupled from the implementation. The features are totally portable.
This import-export structure works continuously if you’d like to nest your features further.
A conclusion, of sorts
No matter which tips you decide to incorporate into your own development practices, keep in mind that these are only suggestions to accomplish the end goal of making writing correct code easy. Take what works for your own uses, and leave what doesn’t!
Thoughts from the crowd?
Disagree with something? Have ideas on improvements? Have your own tips and tricks you’ve found in developing in the Typescript-Redux stack? Let me know in the comments!
Footnotes:
What about selectors? This could be a whole other article on its own, but I’ll try to put some brief thoughts here. Trivial selectors aren’t as useful in Typescript projects because the strong typing makes state changes and state selection (like state.foo.bar
) pretty easy. For selectors that encapsulate logic, this adds an additional selectors
export to the usual set, and the importing Redux file must wrap the selectors with the corresponding state key.
UPDATE: Here’s a gist that may help you.
What about sagas? Again, using Typescript and redux-saga could be another article on its own, but again some brief thoughts. A Redux file that uses sagas should have an additionalsaga
export, and the importing file can use the same strategy to combine sagas as combining reducers, using fork
instead of combineReducers
.
UPDATE: Check out the discussion here for some insights in combining sagas.