with Typescript.

No matter what people have said I think Redux is still an awesome way to manage state with React. I really enjoy how it makes testing easier, separates application logic from the presentation and how easy it is to debug using the Redux DevTools. I’ve written dozens of apps using it and I will write more in future.

Main issue people seem to have with Redux is the amount of boilerplate required make anything useful with it and I share that complaint too. But I do think it’s solved by the community extensions when using vanilla Javascript as there are many options to choose from.

Unfortunately with Typescript the situation is not so good. Setting up actions and reducers and connecting the state to React components in type-safe way is really painful and there are no good existing solutions that I know of. Either you have to give up on some of the type-safety or write even more boilerplate. I’ve struggled with this so much that I end up creating two libraries to solve boilerplate and type-safety issues.

Actions and Reducers

First issue here is making immutable updates in reducers in type-safe way. You can get by the using object spread operator but when doing deeply nested updates it gets really verbose. Luckily I’ve found Immer to be really good solution to this. It allows you to make immutable updates just like you would do mutable updates which is really good for Typescript because you can leverage all the type-safety features Typescript provides with it (strict null checks etc.). The down-side is of course the same thing. It can get bit confusing on where you can I use the mutable-immutable style updates once you start extracting reducer logic to functions outside of the Immer produce() function but I think the trade-off is well worth it.

Michel Weststrate’s Immer in action

If you are concerned by the down-side you can define Redux state as completely read-only like in the example. Immer can then remove the read-only flags inside its produce() function. This way it’s impossible to make mistakes regarding this.

Building on Immer I’ve created Immer Reducer which allows you define reducers as classes where each method becomes an Action Type, Action Object, Action Creator and Reducer. All in type-safe way without boilerplate. Although at the moment classes are not too cool in React world with the new Hooks API et al. but it was the only way I was able to achieve proper type-safety. Regardless It works really well and is terse in comparison to many alternatives.

Immer Reducer with the generated action type. Note how the payload tuple type is infered from the method arguments. Multiple arguments are supported too. This is powered by conditional types and tuples in rest parameters.

The runtime implementation is really simple. Even the redux-starter-kit comes with something like this called createReducer() which is also using Immer.

The non-trivial part of the this library was the type implementation and it was only recently when this became possible with Typescript. It uses conditional types and tuples in function rest parameters to achieve this. Which means fairly recent version of Typescript is required. Tested with 3.1 but 3.0 might work too. But do note that this internal type complexity is not exposed to the end user in any way.

So the point in Immer Reducer is the type-safety and Typescript-first design in general.

Check it out on Github

Connecting State to Components

React Redux comes with types for the connect() higher-order component but it has some caveats even so much so that I’ve read some somewhere that the maintainers are considering removing them from the core libary and moving them to Definitely Typed for more community driven approach. Which I can totally understand because AFAIK the maintainers are not themselves using Typescript so it’s quite a burden to maintain them if you don’t have deep Typescript knowledge.

But even if the connect() were typed perfectly there’s still this fundamental issue that you must type the props manually for the component you are going to wrap introducing some boilerplate code.

React Redux connect() with types

A better approach for Typescript is to inverse the connect() and component relationship by using the old-new* render props pattern where the render callback can infer the types from the data providing component.

Render Props with mapped state type inference

So for this I’ve created Redux Render Prop which implements this. The idea is that you create these Render Prop Connect Components each time you need different part of the Redux state. Keeping these Connect Components small and close to where it’s rendered ensures good rendering performance.

It’s not complete rewrite of connect() as it actually uses the connectAdvanced() primitive under the hood from React Redux on which connect() is build too.

Check it out on Github as well

*The new React Hooks API actually seems really good for static typing but for now it’s impossible implement useRedux() style hook with reasonable performance. This issue is tracked in #14110 in the React repository and hooks general for Redux in #1063. But hey — the Hooks are still in alpha stage anyways :) Regardless I’m really looking forward for a good hooks implementation for Redux.

Update! I released TypeScript friendly Redux Hooks. And yes, it’s really good!

Full Example

Here’s an example on how to organize an app and combine both Immer Reducer and Redux Render Prop

https://github.com/epeli/typescript-redux-todoapp

Thunks?

Async calls and other side effects are important part of any application. I’ve have not found or come up with any so good solutions for Typscript that I can recommend now but I’ve been playing with this API:

Experimental typed Redux thunks API

You can find the implementation here but as stated in the README I don’t recommend others using it even though it’s already released to npm. I’ll probably change the API at some point. But feel free to try it and provide feedback if you feel it’s something worth while.

Conclusion

I’ve written these tools to scratch my own itch on my free time but at this point I have also shipped all of this to production including the thunk implementation at my workplace in multiple projects. I can honestly say they have made my work day quality of life much better but that’s what types do for developers ;)

The main reason to open source this was to motivate myself to document and write proper tests for these so when a new team members join projects using these they can have some proper docs to read including this blog post for motivation why they were created.

I think Typescript is the future of large scale Javascript application development and as bonus I hope these Typescript-first Redux libararies will help its adoption in the Redux community. Both libraries are standalone and incrementally adoptable but they do work very well together.

Thanks for reading this far — Happy hacking!